zoukankan      html  css  js  c++  java
  • poj2914-Minimum Cut

    题意

    (n) 个点 (m) 条边的无向带权图求全局最小割。(nle 500,mle frac{n(n-1)}{2})

    分析

    参考了 这篇博客,去给他点赞。

    嘛,今天研究了一下全局最小割。

    全局最小割是什么呀?

    运用经典的最大流最小割,我们可以在网络流复杂度内求出对于两个点 (s,t) ,把图分成 (sin S) 集和 (tin T) 集的需要去掉的最小边权和。我们称这种割为对于一组点 ((s,t))(s-t) 割。

    全局最小割,就是把整个无向图割开,却不指定怎么割,求最小边权和。

    用之前的方法,(O(n*网络流)) 可以分治得到最小割树,从而求出任意两点间的最小割,那么取最小边权就是答案。但已知理论复杂度比较优秀的网络流算法复杂度也达到 (O(n^2sqrt m)) (最高标号预留推进),再乘上 (n) ,这是一个很高的复杂度。

    是否有办法优化呢?这个问题中 不指定要割什么 这个条件并没有用上,可以从这里入手。

    下面就来介绍全局最小割的 Stoer-Wagner 算法。

    整体思路

    解决这个问题,有一个关键的性质需要利用。

    (s,t) 为图中两点,那么在任意一个割中,它们要么在同一个集合中,要么在不同的集合中。

    算法的整体思路是,我们不指定割开哪两个点,而是设计一个函数 (f(G)) ,返回一个三元组 ((s,t,c)) ,表示这个图中 ((s,t)) 的最小割为 (c) 。注意,这个函数告诉我们它割开哪两个点,而不是我们告诉它 。利用上面的性质,要么这个图的全局最小割要么就是 (c) ,要么 (s,t) 在同一集合中。

    为什么是这样呢?显然图的全局最小割一定小于等于 (c) ,若全局最小割下 (s,t) 在不同集合中,而全局最小割却小于 (c) ,那么必然存在更小的 (s-t) 割,这与 (c)(s-t) 最小割矛盾。

    我们把答案对 (c)(min),接下来就讨论 (s,t) 在同一集合中的情况。若是这样,那么其实可以把 (s,t) 并起来,因为 (s)(t) 中间的边是不会割掉的。所以就把 (s,t) 并起来,把边合并就好啦!

    这样进行,直到图中只剩下一个点,我们就得到了答案。显然上面的过程进行了 (n-1) 次,所以复杂度为 (O(n(m+f))) 。接下来只要我们能够有一个函数,快速地告诉我们一对点间的最小割,问题就解决啦。

    函数 (f(G))

    算法流程

    • 有一个空集 (A) ,最开始在 (G) 中任意找一个点放进 (A)
    • 不断在 (G) 中找到一个点 (v otin A) 使得它到 (A) 中所有连边权值和最大,把这个点加入 (A) ,直到 (A=V)
    • 倒数第二个加入 (A) 和最后加入 (A) 的两点即分别为 (s,t) ,它们的最小割是 (t)(V-lbrace t brace) 的边权和。

    下面证明这个算法的正确性。实际上要说明的是,对于任意一个点集的划分 (V=S+T) 使得 (sin S,tin T) ,有 (cut(V-lbrace t brace,lbrace t brace)le cut(S,T))

    一些记号

    • (w(e)) ,边 (e) 的权值;(w(x,y)) ,边 ((x,y)) 的权值
    • (w(S,x)=sum _{vin S,(x,v)in E}w(x,v))
    • (C) ,对于点集的划分 (S,T) 的最小割
    • (a) ,加入 (A) 的点的序列,(a_i) 表示第 (i) 个加入 (A) 的点
    • (A_x) ,加入 (x) 之前加入 (A) 的点的集合,不包含 (x)
    • (C_x)(lbrace (u,v)|u,vin A_xcaplbrace x brace,(u,v)in C brace) 。此处 (C) 就是上面的那个,即 (C)(A_xcap lbrace x brace) 中的诱导割。
    • (Bsetminus C)(B) 集合中去掉集合 (C) 剩下的集合,即 (C)(B) 中的补集。

    接下来要证明,对于所有点 (v) 满足 (a) 中排 (v) 前面的点与 (v) 不在割 (C) 的同一侧,有 (w(A_v,v)le C_v) 。若能得到这个,由于 (t) 是满足这个条件的,就有 (w(A_t,t)=w(V-lbrace t brace,t)=cut(V-lbrace t brace,lbrace t brace)le C_t) ,即得到上面的结论。

    对第一个满足条件的 (v) ,等号成立,因为 (v) 是第一个不与前面在同一集合中的点,所以 (C_v) 就是 (w(A_v,v)) ,这些边是一定要割掉的。下面对 (v) 用归纳法。

    设对于一个满足条件的 (v) 以及前面满足条件的点,结论都成立,那么对于 (v) 的下一个点 (u) ,说明这个结论成立。

    首先有 (w(A_u,u)=w(A_v,u)+w(A_usetminus A_v,u)) ,这是显然的,因为它是对集合 (A_u) 的一个划分。

    由归纳假设可得,(w(A_v,v)le C_v) ,又因为算法过程告诉我们 (u)(v) 后面加入,所以在加入 (v) 之前一刻,(v)(A_v) 的连边权值和大于 (u)(A_v) 连边的权值和,所以有 (w(A_v,u)le w(A_v,v)) ,于是得到:

    [egin{aligned} w(A_v,u)le w(A_v,v)le C_v && (1) end{aligned} ]

    (C_u) 的含义,是在一个 ((S,T)) 割中要把 (A_ucap lbrace u brace) 割成两部分的那部分。这一定包含了 (C_v) ,因为 (v) 与之前的那个也不再同一个集合中。(w(A_usetminus A_v,u)) 一定是要割掉的,否则就无法保证 (u) 与之前的那个不在同一集合中。于是得到:

    [egin{aligned} C_v+w(A_usetminus A_v,u)le C_u && (2) end{aligned} ]

    联立上两式,得到:

    [w(A_u,u)=w(A_v,u)+w(A_usetminus A_v,u)le C_u ]

    这样我们证明了结论。

    函数 (f) 的复杂度直接做是 (O(m+n^2)) ,可以用斐波那契堆优化到 (O(m+nlog n)) (普通堆是 (O((m+n)log n)) ,在稠密图中与 (O(m+n^2)) 没有什么区别)。因此整个算法的复杂度为 (O(nm+n^3))(O(nm+n^2log n))

    代码

    #include<cstdio>
    #include<cctype>
    #include<climits>
    #include<cstring>
    #include<algorithm>
    #define M(x) memset(x,0,sizeof x)
    using namespace std;
    inline int read() {
    	int x=0,f=1;
    	char c=getchar();
    	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*f;
    }
    const int maxn=1e3+1;
    int n,m;
    namespace graph {
    	int d[maxn],f[maxn][maxn],ed;
    	bool no[maxn],ina[maxn];
    	inline void clear() {M(no),M(f);}
    	inline void add(int x,int y,int w) {
    		f[x][y]+=w;
    	}
    	void newlink(int nw,int s,int t) {
    		for (int v=1;v<=ed;++v) if (!no[v] && v!=t) {
    			add(nw,v,f[s][v]);
    			add(v,nw,f[s][v]);
    		}
    	}
    	inline void push(int x) {
    		ina[x]=true;
    		for (int v=1;v<=ed;++v) if (!no[v] && !ina[v]) d[v]+=f[x][v];
    	}
    	int glob(int cs,int &s,int &t) {
    		M(d),M(ina);
    		int a;
    		for (a=1;a<=ed && (no[a] || ina[a]);++a);
    		push(t=a);
    		while (cs--) {
    			int p=0;
    			for (int i=1;i<=ed;++i) if (!no[i] && !ina[i] && d[i]>d[p]) p=i;
    			s=t,t=p;
    			push(p);
    		}
    		return d[t];
    	}
    	int run() {
    		int ret=INT_MAX,here=(n-1)<<1;
    		for (ed=n;ed<=here;++ed) {
    			int s=0,t=0,g=glob((n<<1)-ed-1,s,t);
    			ret=min(ret,g);
    			int nw=ed+1;
    			newlink(nw,s,t);
    			newlink(nw,t,s);
    			no[s]=no[t]=true;
    		}
    		return ret;
    	}
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    #endif
    	while (~scanf("%d%d",&n,&m)) {
    		graph::clear();
    		for (int i=1;i<=m;++i) {
    			int x=read()+1,y=read()+1,w=read();
    			graph::add(x,y,w),graph::add(y,x,w);
    		}
    		int ans=graph::run();
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    VS-Visual Studio-IIS Express 支持局域网访问
    JAVA和C# 3DES加密解密
    Js调用Java方法并互相传参
    Cannot find SS.INI file for user *** 解决方法
    $.ajax()方法参数详解
    HANA Studio打开系统显示Secure storage is locked
    C#通过ODBC查询HANA数据库数据
    IIS7发布asp.net mvc提示404.0
    CentOS 搭建git服务
    解决用navicat远程连接数据库出现1045 access denied for user 'root'@'localhost' using password yes
  • 原文地址:https://www.cnblogs.com/owenyu/p/7465062.html
Copyright © 2011-2022 走看看