全局最小割 学习总结
全局最小割的意思是在一个无向图中任取S和T,求最小割的最小值
还有一种描述是删掉无向图中的边使得其不连通的最小代价
当然,这种题目可以用分治+最小割来求解
但是时间复杂度大约在O(n^4)左右
有一种更好的求解方法可以在O(n^3)的时间复杂度内求解
做法是这样的:
首先对于图中任意两点S->T
要么S和T不在一个集合里时是答案,答案显然是S和T的最小割
否则S和T在一个集合里,我们可以将S和T缩成一个点,不难证明这样是等效的
我们模拟这个过程,每次任取S和T跑最小割,时间复杂度大概跟分治+最小割差不多OwO
但是我们注意到这一步的S和T都是任取的,也就是我们只需要求出一组解就可以了
这样就有了一种构造算法:
1、一开始A集合为空,我们任取一点加入A集合中
2、定义W(A,x)表示A集合所有点到x的边权和
3、每次寻找W(A,x)最大的点(相同的话任取),加入A集合中并更新其他的W值
4、最后加入的两个点则分别是S和T,其最小割为最后加入时W(A,T)
这个过程直接模拟显然是O(n^2)的,加上缩点最多进行O(n)次
所以总时间复杂度O(n^3),可以用堆优化,但是稠密图跑得很慢很慢
BZOJ 3345
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; const int maxn=510; const int oo=0x7fffffff; int n,m,u,v,d; int S,T,cut; int f[maxn][maxn]; int w[maxn]; bool com[maxn],vis[maxn]; void Get_ans(){ cut=oo;S=T=-1; memset(vis,false,sizeof(vis)); memset(w,0,sizeof(w)); for(int i=1;i<=n;++i){ int mx=-oo,tmp; for(int j=1;j<=n;++j){ if(!com[j]&&!vis[j]&&w[j]>mx){ mx=w[j];tmp=j; } } if(T==tmp)return; S=T;T=tmp; cut=mx;vis[tmp]=true; for(int j=1;j<=n;++j)if(!com[j]&&!vis[j])w[j]+=f[tmp][j]; }return; } int Get_SW(){ memset(com,false,sizeof(com)); int ans=oo; for(int i=1;i<n;++i){ Get_ans(); ans=min(ans,cut); if(ans==0)return 0; com[T]=true; for(int j=1;j<=n;++j){ if(!com[j]){f[S][j]+=f[T][j];f[j][S]+=f[j][T];} } }return ans; } int main(){ scanf("%d%d",&n,&m); memset(f,0,sizeof(f)); for(int i=1;i<=m;++i){ scanf("%d%d%d",&u,&v,&d); f[u][v]+=d;f[v][u]+=d; } printf("%d\n",Get_SW()); return 0; }
hdu 3691
注意到这里的S是给定的
但是不难发现这并没有什么卵用,答案还是全局最小割
因为设最后全局最小割中一定有一个T跟S不属于一个集合OwO
又因为是全局最小割,所以这组S->T的最小割就是全局最小割
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int maxn=310; const LL oo=1LL<<60; int n,m,k; int u,v,w; int S,T; LL ans,cut; LL f[maxn][maxn]; LL W[maxn]; bool com[maxn],vis[maxn]; void read(int &num){ num=0;char ch=getchar(); while(ch<'!')ch=getchar(); while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar(); } void Get_ans(){ cut=oo;S=T=-1; memset(vis,false,sizeof(vis)); memset(W,0,sizeof(W)); for(int i=1;i<=n;++i){ LL mx=-oo;int tmp; for(int j=1;j<=n;++j){ if(!com[j]&&!vis[j]&&W[j]>mx){mx=W[j];tmp=j;} } if(T==tmp)return; S=T;T=tmp; cut=mx;vis[tmp]=true; for(int j=1;j<=n;++j){ if(!com[j]&&!vis[j]){W[j]+=f[tmp][j];} } }return; } LL Get_SW(){ ans=oo;memset(com,0,sizeof(com)); for(int i=1;i<n;++i){ Get_ans(); ans=min(ans,cut); if(ans==0)return ans; com[T]=true; for(int j=1;j<=n;++j){ if(!com[j]){f[S][j]+=f[T][j];f[j][S]+=f[j][T];} } }return ans; } int main(){ while(scanf("%d%d%d",&n,&m,&k)==3){ if(!n&&!m&&!k)break; memset(f,0,sizeof(f)); for(int i=1;i<=m;++i){ read(u);read(v);read(w); f[u][v]+=w;f[v][u]+=w; } printf("%d\n",Get_SW()); }return 0; }