题意

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

分析

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

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

全局最小割是什么呀?

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

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

用之前的方法,\(O(n*网络流)\) 可以分治得到最小割树,从而求出任意两点间的最小割,那么取最小边权就是答案。但已知理论复杂度比较优秀的网络流算法复杂度也达到 \(O(n^2\sqrt 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\notin A\) 使得它到 \(A\) 中所有连边权值和最大,把这个点加入 \(A\) ,直到 \(A=V\) 。
  • 倒数第二个加入 \(A\) 和最后加入 \(A\) 的两点即分别为 \(s,t\) ,它们的最小割是 \(t\) 到 \(V-\lbrace t \rbrace\) 的边权和。

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

一些记号

  • \(w(e)\) ,边 \(e\) 的权值;\(w(x,y)\) ,边 \((x,y)\) 的权值
  • \(w(S,x)=\sum _{v\in 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,v\in A_x\cap\lbrace x\rbrace,(u,v)\in C\rbrace\) 。此处 \(C\) 就是上面的那个,即 \(C\) 在 \(A_x\cap \lbrace x\rbrace\) 中的诱导割。
  • \(B\setminus 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\rbrace,t)=cut(V-\lbrace t\rbrace,\lbrace t\rbrace)\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_u\setminus 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)\) ,于是得到:

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

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

\[\begin{aligned}
C_v+w(A_u\setminus A_v,u)\le C_u && (2)
\end{aligned}
\]

联立上两式,得到:

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

这样我们证明了结论。

函数 \(f\) 的复杂度直接做是 \(O(m+n^2)\) ,可以用斐波那契堆优化到 \(O(m+n\log n)\) (普通堆是 \(O((m+n)\log n)\) ,在稠密图中与 \(O(m+n^2)\) 没有什么区别)。因此整个算法的复杂度为 \(O(nm+n^3)\) 或 \(O(nm+n^2\log 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\n",ans);
}
return 0;
}

poj2914-Minimum Cut的更多相关文章

  1. poj2914 Minimum Cut 全局最小割模板题

    Minimum Cut Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 8324   Accepted: 3488 Case ...

  2. POJ2914 Minimum Cut —— 最小割

    题目链接:http://poj.org/problem?id=2914 Minimum Cut Time Limit: 10000MS   Memory Limit: 65536K Total Sub ...

  3. POJ Minimum Cut

    Minimum Cut Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 9302   Accepted: 3902 Case ...

  4. POJ 2914 Minimum Cut

    Minimum Cut Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 9319   Accepted: 3910 Case ...

  5. hdu 5452 Minimum Cut 树形dp

    Minimum Cut Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=54 ...

  6. POJ 2914 Minimum Cut 最小割图论

    Description Given an undirected graph, in which two vertices can be connected by multiple edges, wha ...

  7. HDU 6214.Smallest Minimum Cut 最少边数最小割

    Smallest Minimum Cut Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Oth ...

  8. HDU 6214 Smallest Minimum Cut(最少边最小割)

    Problem Description Consider a network G=(V,E) with source s and sink t. An s-t cut is a partition o ...

  9. Smallest Minimum Cut HDU - 6214(最小割集)

    Smallest Minimum Cut Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Oth ...

  10. HDU - 6214:Smallest Minimum Cut(最小割边最小割)

    Consider a network G=(V,E) G=(V,E) with source s s and sink t t . An s-t cut is a partition of nodes ...

随机推荐

  1. 20155308 2016-2017-2 《Java程序设计》实验二 Java面向对象程序设计

    20155308 2016-2017-2 <Java程序设计>实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UM ...

  2. day1 Opencv安装 python 2.7 (32位)

    [参考安装步骤] http://opencv-python-tutroals.readthedocs.io/en/latest/index.html http://blog.csdn.net/huru ...

  3. CF 1064 D. Labyrinth

    D. Labyrinth http://codeforces.com/contest/1064/problem/D 题意: n*m的矩阵,只能往左走l次,往右走r次,上下走无限制,问能走到多少个点. ...

  4. 安装centos minimal 版本后的网络配置(linux)

    1.修改网卡配置文件 2.重启网络服务 3.测试网络

  5. 微信小程序模板消息群发解决思路

    基于微信的通知渠道,微信为开发者提供了可以高效触达用户的模板消息能力,以便实现服务的闭环并提供更佳的体验.(微信6.5.2及以上版本支持模板功能.低于该版本将无法收到模板消息.) 模板推送位置:服务通 ...

  6. Base64编码后通过Url传值

    Base64编码简介 Base编码使用"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",再加上补 ...

  7. 添加jQuery方法解析url查询部分

    Web前端不同页面间传值可以使用 cookies.localStorage 和 sessionStorage 等本地存储. 但是,今天我们尝试使用 url 查询,假设我们要传递字符串 str 到 mo ...

  8. Selenium2+python自动化-环境搭建

    一.selenium简介 Selenium 是用于测试 Web 应用程序用户界面 (UI) 的常用框架.它是一款用于运行端到端功能测试的超强工具.您可以使用多个编程语言编写测试,并且 Selenium ...

  9. 第k小分数(二分值)

    //时间限制:10000ms //单点时限:1000ms //内存限制:256MB //描述 //给定N个不同的质数P1, P2, … PN.用它们作为分目可以组成(P1-1) + (P2-1) + ...

  10. 换Mac了,迈入了终端的大门

    多终端其实本质和多线程一样,所有终端其实都共享着同一个内存只不过不同终端对共享内存不同部分的权限不同罢了所以对终端的数量必须要有限制 我这里开启了四个线程,很明显四个线程都在跑同一个内存而且四个线程都 ...