生活随笔
收集整理的這篇文章主要介紹了
最小割小记
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
參考博客:最小割淺談
關(guān)于最小割
常用描述
表述一:刪去若干條邊使得源點(diǎn)到匯點(diǎn)不連通,求刪邊的權(quán)值和的最小可能值。
表述二:將點(diǎn)集分為(S,T)(S,T)(S,T),記所有從SSS中出發(fā)到TTT中的邊的權(quán)值和為c(S,T)c(S,T)c(S,T),求c(S,T)c(S,T)c(S,T)的最小值。
求最小割
a. 以權(quán)值為容量,該網(wǎng)絡(luò)最大流的值即為最小割的值
b. 在殘量網(wǎng)絡(luò)中,從源點(diǎn)出發(fā)進(jìn)行一次增廣BFS,即得到一個(gè)分割。該分割是一個(gè)最小割。
題型1:對(duì)求最小割原理的理解
[AHOI2009]最小割
摘自此Blog
[SDOI2014]LIS
摘自此Blog
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define maxn 1505
#define LL long long
#define inf 999999999
inline int max(int a
,int b
){return a
>b
?a
:b
;}
inline int min(int a
,int b
){return a
<b
?a
:b
;}
inline int read()
{char c
=getchar();int x
=0;while(c
<'0'||c
>'9') c
=getchar();while(c
>='0'&&c
<='9') x
=(x
<<3)+(x
<<1)+c
-48,c
=getchar();return x
;
}
struct node{int a
,b
,c
,rk
;}g
[maxn
];
inline int cmp(node A
,node B
) {return A
.c
<B
.c
;}
struct E{int v
,nxt
,w
,f
;}e
[maxn
*maxn
];
int n
,num
=1,S
,T
,Test
;
int ans
[maxn
],cnt
,id
[maxn
],vis
[maxn
];
int d
[maxn
],dp
[maxn
],head
[maxn
],cur
[maxn
],in
[maxn
],out
[maxn
];
inline void add(int x
,int y
,int z
) {e
[++num
].v
=y
;e
[num
].nxt
=head
[x
];head
[x
]=num
;e
[num
].w
=z
;}
inline void C(int x
,int y
,int z
) {add(x
,y
,z
),add(y
,x
,0);}
inline int check(int s
,int t
)
{std
::queue
<int> q
;memset(vis
,0,sizeof(vis
));q
.push(s
);vis
[s
]=1;while(!q
.empty()){int k
=q
.front();q
.pop();if(k
==t
) return 1;for(re
int i
=head
[k
];i
;i
=e
[i
].nxt
)if(!vis
[e
[i
].v
]&&e
[i
].w
>e
[i
].f
) q
.push(e
[i
].v
),vis
[e
[i
].v
]=1;}return 0;
}
inline int BFS(int s
,int t
)
{std
::queue
<int> q
;memcpy(cur
,head
,sizeof(head
));memset(d
,0,sizeof(d
));d
[s
]=1,q
.push(s
);while(!q
.empty()){int k
=q
.front();q
.pop();for(re
int i
=head
[k
];i
;i
=e
[i
].nxt
)if(!d
[e
[i
].v
]&&e
[i
].w
>e
[i
].f
) {d
[e
[i
].v
]=d
[k
]+1;if(e
[i
].v
==t
) return 1;q
.push(e
[i
].v
);}}return d
[t
];
}
int dfs(int x
,int now
,int t
)
{if(x
==t
||!now
) return now
;int flow
=0,ff
;for(re
int& i
=cur
[x
];i
;i
=e
[i
].nxt
)if(d
[e
[i
].v
]==d
[x
]+1){ff
=dfs(e
[i
].v
,min(now
,e
[i
].w
-e
[i
].f
),t
);if(ff
<=0) continue;now
-=ff
,flow
+=ff
;e
[i
].f
+=ff
,e
[i
^1].f
-=ff
;if(!now
) break;}return flow
;
}
int main()
{Test
=read();while(Test
--){n
=read();cnt
=0;num
=1;memset(head
,0,sizeof(head
));for(re
int i
=1;i
<=n
;i
++) g
[i
].a
=read(),dp
[i
]=1;for(re
int i
=1;i
<=n
;i
++) g
[i
].b
=read(),g
[i
].rk
=i
;for(re
int i
=1;i
<=n
;i
++)for(re
int j
=1;j
<i
;j
++) if(g
[j
].a
<g
[i
].a
) dp
[i
]=max(dp
[j
]+1,dp
[i
]);int tot
=0;for(re
int i
=1;i
<=n
;i
++) tot
=max(tot
,dp
[i
]);T
=0;for(re
int i
=1;i
<=n
;i
++) in
[i
]=++T
;for(re
int i
=1;i
<=n
;i
++) out
[i
]=++T
;++T
;for(re
int i
=1;i
<=n
;i
++) C(in
[i
],out
[i
],g
[i
].b
),id
[i
]=num
;for(re
int i
=1;i
<=n
;i
++) if(dp
[i
]==1) C(S
,in
[i
],inf
);for(re
int i
=1;i
<=n
;i
++) if(dp
[i
]==tot
) C(out
[i
],T
,inf
);for(re
int i
=1;i
<=n
;i
++)for(re
int j
=1;j
<i
;j
++) if(g
[j
].a
<g
[i
].a
&&dp
[j
]+1==dp
[i
]) C(out
[j
],in
[i
],inf
);tot
=0;while(BFS(S
,T
)) tot
+=dfs(S
,inf
,T
);for(re
int i
=1;i
<=n
;i
++) g
[i
].c
=read();printf("%d ",tot
);std
::sort(g
+1,g
+n
+1,cmp
);for(re
int i
=1;i
<=n
;i
++){int k
=g
[i
].rk
;if(check(in
[k
],out
[k
])) continue;ans
[++cnt
]=k
;while(BFS(T
,out
[k
])) dfs(T
,inf
,out
[k
]);while(BFS(in
[k
],S
)) dfs(in
[k
],inf
,S
);e
[id
[k
]].w
=e
[id
[k
]^1].w
=0;e
[id
[k
]].f
=e
[id
[k
]^1].f
=0;}printf("%d\n",cnt
);std
::sort(ans
+1,ans
+cnt
+1);for(re
int i
=1;i
<=cnt
;i
++) printf("%d ",ans
[i
]);putchar(10);}return 0;
}
題型2:最下生成樹相關(guān)
例題這里有
題型3:對(duì)點(diǎn)的分割 轉(zhuǎn) 對(duì)邊的分割
[HNOI2013] 切糕
題型4:最小點(diǎn)割集
最小點(diǎn)割集是指:
給出一張有向圖(無向圖)和兩個(gè)點(diǎn)S、T,每個(gè)點(diǎn)都有一個(gè)正數(shù)點(diǎn)權(quán),求一個(gè)不包含點(diǎn)S、T的權(quán)值和最小的點(diǎn)集使得刪掉點(diǎn)集中的所有點(diǎn)后,S無法到達(dá)T。
求法:
對(duì)于這個(gè)問題,我們將每一個(gè)點(diǎn)拆成兩個(gè)點(diǎn),一個(gè)為入點(diǎn),一個(gè)為出點(diǎn),這兩個(gè)點(diǎn)之間有一條邊權(quán)為原圖中點(diǎn)權(quán)的有向邊,從入點(diǎn)指向出點(diǎn)。對(duì)于原圖中的邊x→yx\to yx→y,我們將其更改為x的出點(diǎn)→y\to y→y的入點(diǎn)。在轉(zhuǎn)化完的圖上跑最小割就是原圖的最小點(diǎn)割集。
題型5:最小割樹——求任意兩點(diǎn)的最小割
最小割樹
#include<bits/stdc++.h>
using namespace std
;
int n
,m
,node
[505],dep
[505],fa
[505][10],mn
[505][10];
int cnt
,top
[505],to
[1005],len
[1005],nex
[1005];
int read()
{int re
=0;char ch
=getchar();while(!isdigit(ch
)) ch
=getchar();while(isdigit(ch
)) re
=(re
<<3)+(re
<<1)+ch
-'0',ch
=getchar();return re
;
}
void add_edge(int x
,int y
,int z
)
{to
[++cnt
]=y
,len
[cnt
]=z
,nex
[cnt
]=top
[x
],top
[x
]=cnt
;to
[++cnt
]=x
,len
[cnt
]=z
,nex
[cnt
]=top
[y
],top
[y
]=cnt
;
}
namespace GHT
{int s
,t
;int tot
,cur
[505],dep
[505],col
[505],col_bucket
[505];int cnt
=1,top
[505],to
[3005],cap
[3005],flow
[3005],nex
[3005];void add_edge(int x
,int y
,int z
){to
[++cnt
]=y
,cap
[cnt
]=z
,flow
[cnt
]=0,nex
[cnt
]=top
[x
],top
[x
]=cnt
;to
[++cnt
]=x
,cap
[cnt
]=z
,flow
[cnt
]=0,nex
[cnt
]=top
[y
],top
[y
]=cnt
;}bool BFS(){memset(cur
,0,sizeof cur
);memset(dep
,0,sizeof dep
);dep
[s
]=1,cur
[s
]=top
[s
];queue
<int>Q
;Q
.push(s
);while(!Q
.empty()){int now
=Q
.front();Q
.pop();for(int i
=top
[now
];i
;i
=nex
[i
])if(!dep
[to
[i
]]&&cap
[i
]>flow
[i
]){dep
[to
[i
]]=dep
[now
]+1;cur
[to
[i
]]=top
[to
[i
]];Q
.push(to
[i
]);}}return dep
[t
]!=0;}int DFS(int now
,int rest
){if(now
==t
) return rest
;int re
=0;for(int &i
=cur
[now
];i
;i
=nex
[i
])if(dep
[to
[i
]]==dep
[now
]+1&&cap
[i
]>flow
[i
]){int lzq
=DFS(to
[i
],min(rest
,cap
[i
]-flow
[i
]));if(lzq
){rest
-=lzq
,re
+=lzq
;flow
[i
]+=lzq
,flow
[i
^1]-=lzq
;if(!rest
) break;}}return re
;}int Dinic(int x
,int y
){int re
=0;s
=x
,t
=y
;for(int i
=1;i
<=cnt
;i
++) flow
[i
]=0;while(BFS()) re
+=DFS(s
,0x3f3f3f3f);return re
;}void get_color(int now
,int color
){col
[now
]=color
;for(int i
=top
[now
];i
;i
=nex
[i
])if(cap
[i
]>flow
[i
]&&col
[to
[i
]]!=color
)get_color(to
[i
],color
);}void build(int l
,int r
){if(l
==r
) return ;int x
=node
[l
],y
=node
[l
+1];int cut
=Dinic(x
,y
);get_color(x
,++tot
);int L
=l
,R
=r
;for(int i
=l
;i
<=r
;i
++)if(col
[node
[i
]]==tot
) col_bucket
[L
++]=node
[i
];else col_bucket
[R
--]=node
[i
];for(int i
=l
;i
<=r
;i
++) node
[i
]=col_bucket
[i
];::add_edge(x
,y
,cut
);build(l
,L
-1);build(R
+1,r
);}
}
void dfs(int now
)
{for(int i
=1;i
<=9;i
++){fa
[now
][i
]=fa
[fa
[now
][i
-1]][i
-1];mn
[now
][i
]=min(mn
[now
][i
-1],mn
[fa
[now
][i
-1]][i
-1]);}for(int i
=top
[now
];i
;i
=nex
[i
]){if(to
[i
]==fa
[now
][0]) continue;dep
[to
[i
]]=dep
[now
]+1,fa
[to
[i
]][0]=now
,mn
[to
[i
]][0]=len
[i
];dfs(to
[i
]);}
}
int getcut(int x
,int y
)
{int re
=INT_MAX
;if(dep
[x
]<dep
[y
]) swap(x
,y
);for(int i
=9;i
>=0;i
--) if(dep
[fa
[x
][i
]]>=dep
[y
]) re
=min(re
,mn
[x
][i
]),x
=fa
[x
][i
];if(x
==y
) return re
;for(int i
=9;i
>=0;i
--) if(fa
[x
][i
]!=fa
[y
][i
]) re
=min(re
,min(mn
[x
][i
],mn
[y
][i
])),x
=fa
[x
][i
],y
=fa
[y
][i
];return min(re
,min(mn
[x
][0],mn
[y
][0]));
}
int main()
{n
=read(),m
=read();while(m
--){int x
=read(),y
=read(),z
=read();GHT::add_edge(x
,y
,z
);}for(int i
=1;i
<=n
;i
++) node
[i
]=i
;GHT::build(1,n
);dep
[1]=1;dfs(1);m
=read();while(m
--){int x
=read(),y
=read();printf("%d\n",getcut(x
,y
));}return 0;
}
題型6:最大權(quán)閉合子圖
閉合子圖指的是,對(duì)于有向圖,我們選擇一些點(diǎn),在這個(gè)點(diǎn)集之中,沒有一個(gè)點(diǎn)的出邊指向非此點(diǎn)集中的點(diǎn),但是可以有其他點(diǎn)的出邊指向這個(gè)點(diǎn)集之中。所說的最大權(quán)閉合子圖,就是在這個(gè)圖的所有閉合子圖中點(diǎn)權(quán)和最大的。
求法:
建立源點(diǎn),向每一個(gè)點(diǎn)權(quán)為正的點(diǎn)連邊,邊權(quán)為該點(diǎn)的權(quán)值。建立匯點(diǎn),向每一個(gè)點(diǎn)權(quán)為負(fù)連邊,邊權(quán)為該點(diǎn)的權(quán)值的絕對(duì)值。原圖中的邊進(jìn)行保留,邊權(quán)為infinfinf。最大權(quán)閉合子圖就是所有的點(diǎn)權(quán)為正的點(diǎn)權(quán)和減去最小割。
題型7:劃分點(diǎn)集
記(u→v,w)(u\to v,w)(u→v,w)為一條從uuu到vvv,權(quán)值為www的邊。
基礎(chǔ)模型
把nnn個(gè)點(diǎn)劃分到兩個(gè)集合A,BA,BA,B。給出若干形如 “若…,則有…的代價(jià)/貢獻(xiàn)” 的條件。問最大貢獻(xiàn)/最小代價(jià)是多少。
如果題目問的是最小代價(jià),直接按下面方法連邊求最小割。
如果題目問的是最大貢獻(xiàn),答案為∑所有可能貢獻(xiàn)?最小代價(jià)(最小割)\sum 所有可能貢獻(xiàn)-最小代價(jià)(最小割)∑所有可能貢獻(xiàn)?最小代價(jià)(最小割)。
定義和SSS相連的點(diǎn)劃到AAA集合,和TTT相連的點(diǎn)劃到BBB集合,那么我們可以按下面的方法處理題目給出的條件:
條件-表述1:若把iii劃到AAA,則要付出bib_ibi?的代價(jià);若把iii劃到BBB,則要付出aia_iai?的代價(jià)
條件-表述2:若把iii劃到BBB,則有bib_ibi?的貢獻(xiàn);若把iii劃到AAA,則有aia_iai?的貢獻(xiàn)
方案:(i→T,bi)(i\to T,b_i)(i→T,bi?),(S→i,ai)(S\to i,a_i)(S→i,ai?)條件-表述1:若點(diǎn)集XXX中有元素劃到BBB,則要付出ccc的代價(jià)
條件-表述2:若點(diǎn)集XXX中元素全部劃到AAA,則有ccc的貢獻(xiàn)
方案:(S→new,c),?i∈X(new→i,inf)(S\to new,c),\forall i\in X(new\to i,inf)(S→new,c),?i∈X(new→i,inf)條件-表述1:若點(diǎn)集XXX中有元素劃到AAA,則要付出ddd的代價(jià)
條件-表述2:若點(diǎn)集XXX中元素全部劃到BBB,則有ddd的貢獻(xiàn)
方案:?i∈X(i→new,inf),(new→T,d)\forall i\in X(i\to new,inf),(new\to T,d)?i∈X(i→new,inf),(new→T,d)條件:若點(diǎn)集XXX中有元素劃到BBB,點(diǎn)集YYY中有元素劃到AAA,則要付出eee的代價(jià)
常見形式:若點(diǎn)iii劃到BBB,點(diǎn)jjj劃到AAA,則要付出eee的代價(jià)
方案:?i∈X(new1→i,inf)\forall i\in X(new1\to i,inf)?i∈X(new1→i,inf),?j∈Y(j→new2,inf)\forall j\in Y(j\to new2,inf)?j∈Y(j→new2,inf),(new2→new1,e)(new2\to new1,e)(new2→new1,e)條件:若點(diǎn)集XXX中有元素劃到AAA,點(diǎn)集YYY中有元素劃到BBB,則要付出fff的代價(jià)
常見形式:若點(diǎn)iii劃到AAA,點(diǎn)jjj劃到BBB,則要付出fff的代價(jià)
方案:?i∈X(i→new1,inf)\forall i\in X(i\to new1,inf)?i∈X(i→new1,inf),?j∈Y(new2→j,inf)\forall j\in Y(new2\to j,inf)?j∈Y(new2→j,inf),(new1→new2,f)(new1\to new2,f)(new1→new2,f)
[Shoi2007]Vote 善意的投票
BZOJ3438: 小M的作物
[BZOJ3218]a + b Problem
處理變形問題的trick:黑白染色,翻轉(zhuǎn)源匯
對(duì)于“x,yx,yx,y必須劃到不同集合” “x,yx,yx,y劃到不同集合有ccc的貢獻(xiàn)” “x,yx,yx,y劃到相同集合有ddd的代價(jià)”這樣的條件,我們可以連邊(x,y)(x,y)(x,y),黑白染色后x,yx,yx,y一定一個(gè)是黑點(diǎn),一個(gè)是白點(diǎn)。
如果將其中一種顏色的點(diǎn)進(jìn)行翻轉(zhuǎn)源匯,即原本應(yīng)該連向SSS的邊連向TTT,原本應(yīng)該連向TTT的邊連向SSS,那么“x,yx,yx,y在不同集合”就變成了“x,yx,yx,y在相同集合”,“x,yx,yx,y在相同集合”就變成了“x,yx,yx,y在不同集合”
BZOJ1324Exca王者之劍&BZOJ1475方格取數(shù)——二分圖最大獨(dú)立集
[BZOJ2132]圈地計(jì)劃——翻轉(zhuǎn)源匯
總結(jié)
以上是生活随笔為你收集整理的最小割小记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。