最小割学习小结

一直对最小割模型的理解不够深刻,这段时间学习、总结了一下。

【强烈推荐胡伯涛和彭天翼的论文】

总结几点:

(1)跑完一遍S-T最大流后,在最小割[S,T]中的边必定都是满流的边,但满流的边不一定都是最小割中的边。

(2)最小割的任意方案:跑一遍S-T最大流,然后在残余网络中S能够达到的点为一个割集,剩下的点为另一个割集。

(3)最小割方案的唯一性:跑一遍S-T最大流,如果在残余网络中任意一个点都能从S或者T出发达到,那么最小割方案有唯一性;否则没有唯一性。

(4)跑一遍S-T最大流后,残余网络中所有S能够达到的点必定在S割集中;残余网络中所有T能够达到的点必定在T割集中。

(5)跑一遍S-T最大流后。满足:

若边(u,v)满流,且删掉该边后在残余网络中找不到u到v的路径,则(u,v)可能出现在某个最小割方案中。

若边(u,v)满流,且在残余网络中s能够达到u,v能够达到t,则(u,v)必定出现在任意一个最小割方案中。

例题:

BZOJ 1797 [Ahoi2009]Mincut 最小割

这道题就是上面第(5)点的一个直接应用。那么如何判断某条边是否满足那两个条件呢?

跑完最大流后,在残余网络中求一遍强连通分量。对于某条满流的边(u,v),若u和v不在同一个强连通分量中,则满足第一个条件。若u和S在同一个强连通分量中且v和T在同一个强连通分量中,则满足第二个条件。

  1 #include <stack>
  2 #include <queue>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <algorithm>
  6
  7 using namespace std;
  8
  9 const size_t Max_N(4050);
 10 const size_t Max_M(120050);
 11 const int INF(0X7F7F7F7F);
 12
 13 size_t N;
 14 size_t S, T;
 15
 16 unsigned int Total;
 17 unsigned int Head[Max_N];
 18 unsigned int From[Max_M], To[Max_M], Next[Max_M];
 19 int Cap[Max_M], Flow[Max_M];
 20
 21 stack<size_t> SV;
 22 unsigned int DFS_Clock;
 23 unsigned int Pre[Max_N], Low_Link[Max_N];
 24 unsigned int SCC_Total;
 25 unsigned int SCC_Number[Max_N];
 26
 27 void Get_Val(unsigned int &ret)
 28 {
 29     ret = 0;
 30     char ch;
 31     while ((ch = getchar()), (ch > ‘9‘ || ch < ‘0‘))
 32         ;
 33     do
 34     {
 35         (ret *= 10) += ch - ‘0‘;
 36     }
 37     while ((ch = getchar()), (ch >= ‘0‘ && ch <= ‘9‘));
 38 }
 39
 40 void Get_Val(int &ret)
 41 {
 42     ret = 0;
 43     char ch;
 44     while ((ch = getchar()), (ch > ‘9‘ || ch < ‘0‘))
 45         ;
 46     do
 47     {
 48         (ret *= 10) += ch - ‘0‘;
 49     }
 50     while ((ch = getchar()), (ch >= ‘0‘ && ch <= ‘9‘));
 51 }
 52
 53 inline
 54 void Add_Edge(const size_t &tot, const size_t &s, const size_t &t, const int &c)
 55 {
 56     From[tot] = s, To[tot] = t;
 57     Cap[tot] = c, Flow[tot] = 0;
 58     Next[tot] = Head[s], Head[s] = tot;
 59 }
 60
 61 void init()
 62 {
 63     unsigned int M;
 64     size_t u, v;
 65     int c;
 66
 67     Get_Val(N), Get_Val(M);
 68     Get_Val(S), Get_Val(T);
 69     while (M--)
 70     {
 71         Get_Val(u), Get_Val(v), Get_Val(c);
 72         Total += 2;
 73         Add_Edge(Total, u, v, c);
 74         Add_Edge(Total ^ 1, v, u, 0);
 75     }
 76 }
 77
 78 size_t Cur[Max_N];
 79 unsigned int Dist[Max_N];
 80 bool BFS()
 81 {
 82     memset(Dist, 0, sizeof(Dist));
 83     queue<size_t> Q;
 84     Q.push(S);
 85     Dist[S] = 1;
 86     size_t Top;
 87
 88     while (Q.size())
 89     {
 90         Top = Q.front();
 91         Q.pop();
 92         for (size_t i = Head[Top];i;i = Next[i])
 93             if (!Dist[To[i]] && Cap[i] > Flow[i])
 94             {
 95                 Dist[To[i]] = Dist[Top] + 1;
 96                 Q.push(To[i]);
 97             }
 98     }
 99
100     return Dist[T];
101 }
102
103 int DFS(const size_t &u, int a)
104 {
105     if (u == T || a == 0)
106         return a;
107     int Ans(0), f;
108     for (size_t &i = Cur[u];i;i = Next[i])
109         if (Dist[To[i]] == Dist[u] + 1)
110             if ((f = DFS(To[i], min(a, Cap[i] - Flow[i]))) > 0)
111             {
112                 Ans += f;
113                 a -= f;
114                 Flow[i] += f;
115                 Flow[i ^ 1] -= f;
116                 if (a == 0)
117                     break;
118             }
119     return Ans;
120 }
121
122 void Dinic()
123 {
124     while (BFS())
125     {
126         for (size_t i = 1;i <= N;++i)
127             Cur[i] = Head[i];
128         DFS(S, INF);
129     }
130 }
131
132 void Tarjan(const size_t &u)
133 {
134     Pre[u] = Low_Link[u] = ++DFS_Clock;
135     SV.push(u);
136
137     size_t v;
138     for (size_t i = Head[u];i;i = Next[i])
139         if (Cap[i] > Flow[i])
140         {
141             v = To[i];
142             if (!Pre[v])
143             {
144                 Tarjan(v);
145                 Low_Link[u] = min(Low_Link[u], Low_Link[v]);
146             }
147             else
148                 if (!SCC_Number[v])
149                     Low_Link[u] = min(Low_Link[u], Pre[v]);
150         }
151
152     if (Pre[u] == Low_Link[u])
153     {
154         ++SCC_Total;
155         while (true)
156         {
157             v = SV.top();
158             SV.pop();
159             SCC_Number[v] = SCC_Total;
160             if (u == v)
161                 break;
162         }
163     }
164 }
165
166 int main()
167 {
168     init();
169
170     Dinic();
171
172     for (size_t i = 1;i <= N;++i)
173         if (!Pre[i])
174             Tarjan(i);
175
176     for (size_t i = 2;To[i];i += 2)
177     {
178         unsigned int u = From[i], v = To[i];
179         if (Cap[i] == Flow[i] && SCC_Number[u] != SCC_Number[v])
180             printf("1 ");
181         else
182             printf("0 ");
183         if (Cap[i] == Flow[i] && SCC_Number[u] == SCC_Number[S] && SCC_Number[v] == SCC_Number[T])
184             printf("1");
185         else
186             printf("0");
187         if (To[i + 2])
188             printf("\n");
189     }
190
191     return 0;
192 }

BZOJ 1797

未完待续。

时间: 2024-11-09 00:21:19

最小割学习小结的相关文章

分治最小割 学习总结

这是一种可以较快的求解任意两点间最小割的算法 正常的暴力的话我们要枚举点对,一共要做O(n^2)次网络流 而我们注意到设某一个S->T最小割中两个点x,y,满足x在S集合且y在T集合中 设S->T的最小割为C,x->y的最小割为W 则一定有C>=W 若取得大于号,则x->y的最小割中一定有一个属于S集合点现在属于y集合或者一个属于T集合的点现在属于x集合 这样我们就可以分治处理并每次更新答案 实际上这样操作构成了一棵树,我们称之为最小割树,其任意两点的最小割等价于两点在树上路

全局最小割 学习总结

全局最小割的意思是在一个无向图中任取S和T,求最小割的最小值 还有一种描述是删掉无向图中的边使得其不连通的最小代价 当然,这种题目可以用分治+最小割来求解 但是时间复杂度大约在O(n^4)左右 有一种更好的求解方法可以在O(n^3)的时间复杂度内求解 做法是这样的: 首先对于图中任意两点S->T 要么S和T不在一个集合里时是答案,答案显然是S和T的最小割 否则S和T在一个集合里,我们可以将S和T缩成一个点,不难证明这样是等效的 我们模拟这个过程,每次任取S和T跑最小割,时间复杂度大概跟分治+最小

【最小割】HDU 4971 A simple brute force problem.

说是最大权闭合图.... 比赛时没敢写.... 题意 一共有n个任务,m个技术 完成一个任务可盈利一些钱,学习一个技术要花费钱 完成某个任务前需要先学习某几个技术 但是可能在学习一个任务前需要学习另几个任务 求最多能赚多少钱咯 先将缩点将需要一起学掉的技术缩成一个点 建s--任务 权值为该任务盈利多少钱 建技术(缩点后)-t 权值为学习这技术的花费(总) 任务-技术 (完成该任务所需的每个技术都需要建边)权值为INF #include<stdio.h> #include<stdlib.h

BZOJ 1001: [BeiJing2006]狼抓兔子【最大流/SPFA+最小割,多解】

1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 23822  Solved: 6012[Submit][Status][Discuss] Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的, 而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形: 左上角点为(1,1),右下角点为(N,M)(上图中N=4,M

BZOJ 1001: [BeiJing2006]狼抓兔子 最小割

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1001 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形: 左上角点为(1,1),右下角点为(N,M)(上图中N=4,M=5).有以下三种类型的道路 1:(x,y)<==>(x+1,y) 2:(x,y)<==>(x,y+1) 3:(x,y)

POJ2914 (未解决)无向图最小割|Stoer-Wagner算法|模板

还不是很懂,贴两篇学习的博客: http://www.hankcs.com/program/algorithm/poj-2914-minimum-cut.html http://blog.sina.com.cn/s/blog_700906660100v7vb.html 算法步骤: 1. 设最小割cut=INF, 任选一个点s到集合A中, 定义W(A, p)为A中的所有点到A外一点p的权总和. 2. 对刚才选定的s, 更新W(A,p)(该值递增). 3. 选出A外一点p, 且W(A,p)最大的作为

最大流-最小割 MAXFLOW-MINCUT ISAP

简单的叙述就不必了. 对于一个图,我们要找最大流,对于基于增广路径的算法,首先必须要建立反向边. 反向边的正确性: 我努力查找了许多资料,都没有找到理论上关于反向边正确性的证明. 但事实上,我们不难理解,对于每条反向边,我们流过它相当于撤销了一条正向边的流量. 并且它是必须的: 而且从理论上,我们在加入反向边之后得到的最大流,我们从残余网络考虑. 我们要认识到,反向边不会使最大流流量减少,这是很显然的.有flow<=flow'. 接下来我们考虑所有点的流量是否可以只用正向边得到. 并且我们考察汇

第三章学习小结—-转

[学习目标] 01掌握一维数组的声明和使用方法(OK) 02掌握二维数组的声明和使用方法(OK) 03掌握字符串的声明.赋值.比较和连接方法(连接很少用) 04熟悉字符的ASCII码和ctype.h中的字符函数 05正确认识++.+=等能修改变量的运算符(OK) 06学会用编译选项-Wall获得更多的警告信息(OK) 07了解不同操作系统中换行符的表示方法(嗯) 08掌握fgetc和getchar的使用方法(fgetc基本没用过) 09掌握预处理和迭代开发的技巧(嗯) 程序3-1 逆序输出 输入

最小割 --- [CEOI2008]order

[CEOI2008]order 题目描述: 有N个任务,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序需要某种机器来完成,你可以通过购买或租用机器来完成. 现在给出这些参数,求最大利润 输入格式: 第一行给出 N, M (1 <= N <= 1200, 1 <= M <= 1200) 下面将有N个任务的数据. 每组数据第一行给出完成这个任务能赚到的钱([1,5000])及有多少道工序 接下来若干行每行两个数,分别描述完成工序所需要的机器编号及租用它的费用(