题意

\(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. js获取IE版本,while代码很特别

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  2. 使用WinIO库实现保护模式下的IO和内存读写

    问题已解决: 原因是函数的调用方式与WinIO中不一致,使用的时候漏掉了__stdcall. 函数原定义为: 在实际的GPIO读写中遇到以下问题: SetPortVal可正常写入,但是GetPortV ...

  3. HBase——使用Put迁移MySql数据到Hbase

    先上code: /** * 功能:迁移mysql上电池历史数据到hbase * Created by liuhuichao on 2016/12/6. */ public class MySqlToH ...

  4. 【LG3236】[HNOI2014]画框

    [LG3236][HNOI2014]画框 题面 洛谷 题解 和这题一模一样. 将最小生成树换成\(KM\)即可. 关于复杂度,因为决策点肯定在凸包上,且\(n\)凸包的期望点数为\(\sqrt {\l ...

  5. a data verification error occurred, file load failed

    1. 调试创龙DSP6748的时候,下载.out文件出现这个错误 2. 换了其他板子,还有其他仿真器也不行,最后发现是没加载GEL文件

  6. loj2538 「PKUWC 2018」Slay the Spire

    pkusc 快到了--做点题涨涨 rp. ref我好菜啊QAQ. 可以发现期望只是一个幌子.我们的目的是:对于所有随机的选择方法(一共 \(\binom{2n}{m}\)种),这些选择方法都最优地打出 ...

  7. 基于ejabberd简单实现xmpp群聊离线消息

    首先,xmpp服务器是基于ejabberd.离线消息模块是mod_interact,原地址地址:https://github.com/adamvduke/mod_interact: 修改后实现群聊离线 ...

  8. PHP基础知识试题

    转载于:http://www.php.cn/toutiao-415599.html 1.PHP中传值与传引用的区别,什么时候传值,什么时候传引用? 按值传递:函数范围内对值任何改变在函数外部都会被忽略 ...

  9. JDBC处理大数据

    1.处理大文本 package com.demo; import java.io.File; import java.io.FileNotFoundException; import java.io. ...

  10. 【shell 每日一练7】一键安装mysql5.7,以及密码及策略修改

    一.一键安装Mysql脚本 [root@uat01 ~]# cat InstallMysql01.sh #!/bin/bash #-- #旅行者-Travel #.安装wget yum -y inst ...