• Nubulsa Expo (全局最小割)


    一、前言

    全局最小割板题。

    二、题目

    Vjudge

    求给定源点的无向图最小割。

    三、讲解

    (一)、科技:全局最小割

    首先我们有个连我都能感性理解的算法:任选两个点,求出最小割并更新答案,合并这两个点,最后求出的答案即为全局最小割。这其实就是并不出名大名鼎鼎的 Stoer-Wagner算法。

    由于是任选两个点求最小割,所以我们其实有比跑网络流更快的做法:

    我们搞一个点集 \(A\),初始里面随便放个点,每次选择 \(dis(A,u)=\sum_{a\in A}dis(a,u)\) 最大的点加入点集。

    设第 \(i\) 次加入的点是 \(u_i\),那么 \(cut_{u_{n-1},u_n}\) 割开的点集为 \(\{u_1,u_2,...,u_{n-1}\},\{u_n\}\)

    所以做一次上述做法就可以得到随机两个点的最小割,做 \(n-1\) 次就可以得到全局最小割。

    这个过程可以用类似 Prim 或者说无堆优化的 dijkstra 的方式实现,时间复杂度是 \(O(n^3)\) 的,如果用堆优化,复杂度是 \(O(nm\log_2n)\) 的。

    证明啥的可以去看集训队论文。

    (二)、正题

    之所以叫正题是因为这篇博客本应是题解。

    我们可以注意到这个源点其实没啥用,我们要求的就是全局最小割,证明的话你就考虑全局最小割会把源点 \(S\) 割到一边,在另一边随便选个点其实就是题目所求给定源点的最小割。

    知道这个就可以直接上板子了。

    我用的是 \(O(n^3)\) 的做法。

    四、代码

    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 305;
    const int MAXM = 50005;
    const LL INF = 1ll << 60;
    int n,m;
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    bool vis[MAXN],vis2[MAXN];
    LL e[MAXN][MAXN],dis[MAXN];
    void Add_Double_Edge(int u,int v,int w){
    	e[u][v] += w;
    	e[v][u] += w;
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	while(1){
    		LL ans = INF;
    		n = Read(); m = Read(); Read(); 
    		if(!n && !m) return 0;
    		for(int i = 1;i <= n;++ i) vis[i] = 0;
    		for(int i = 1;i <= n;++ i) for(int j = 1;j <= n;++ j) e[i][j] = 0;
    		for(int i = 1,u,v;i <= m;++ i) u = Read(),v = Read(),Add_Double_Edge(u,v,Read());
    		for(int cas = 1;cas < n;++ cas){//contract 
    			int now = 0,lst = 0,cnt = 0;
    			for(int i = 1;i <= n;++ i){//begin
    				if(!vis[i]) now = i,++cnt;
    				dis[i] = 0;
    			}
    			for(int i = 1;i <= n;++ i) if(!vis[i]) dis[i] = e[now][i],vis2[i] = 0;
    			vis2[now] = 1;
    			for(int i = 1;i < cnt;++ i){
    				lst = now; dis[now] = -1;
    				for(int j = 1;j <= n;++ j) if(!vis2[j] && dis[j] > dis[now]) now = j;
    				vis2[now] = 1;
    				for(int j = 1;j <= n;++ j) if(!vis2[j]) dis[j] += e[now][j];
    			}
    			ans = Min(ans,dis[now]); vis[now] = 1;
    			for(int i = 1;i <= n;++ i) e[lst][i] += e[now][i],e[i][lst] += e[i][now];
    		}
    		Put(ans,'\n');
    	}
    	return 0;
    }
    
  • 相关阅读:
    C++类型转换(字符串)
    GDI+ 中Image::FromStream ,用流的方式显示图像
    mfc对话框序列化实例
    配置android开发环境eclipse获取ADT获取不到(转)
    vs开发错误总结
    MFC获取文件操作
    Android系统架构剖析(转)
    OpenCV 图像采样 插值 几何变换
    C++ char*,char[],string,CString转换
    Simscape Multibody 教程 —— 入门学习
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15921829.html
Copyright © 2020-2023  润新知