您的位置:首页 > 其它

全局最小割 学习总结

2016-07-07 07:26 411 查看
全局最小割的意思是在一个无向图中任取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;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: