整理的算法模板合集: ACM模板


目录

  • 一、集合划分模型
  • 二、点边转化
  • 三、最小割的可行边与必须边
  • 四、二分图的可行边和必须边
  • 五、平面图最小割
  • 六、最小割的一些小技巧
    • 1.记录划分方案
    • 2.求割边数量
    • 3.求最小边的最小割
    • 4.输出任意一种最小割的方案
    • 5.判断一条边是否满流
    • 6.判断某一条边是否可能为最小割中的一条
    • 7.判断某一条边是否为最小割中的一条
    • 8.判断某条边是否一定出现在最小割中

最小割,我们建立一个超级源点和超级汇点,做一下最小割,即可得到通过割边使得整张图变成两个完全不相连的集合的最小花费。

同样的,我们也可以指定两个点为源点和汇点,求得通过割边,使得两个点不再连通的最小花费

最大流最小割定理

最小割 = 最大流

一、集合划分模型

有 n 个物品和两个集合 S,T。将一个物品放入 S 集合会花费 ai,放入 T 集合会花费 bi。还有若干个形如 u,v,w
限制条件,表示如果 u 和 v 同时不在一个集合会花费 w。每个物品必须且只能属于一个集合,求最小的代价。

我们对于每个集合设置源点 S 和汇点 T,第 i 个点由 S 连一条容量为 bi的边、向 T 连一条容量为 ai的边。对于限制条件 u,v,w,我们在 u,v 之间连容量为 w 的双向边。注意到当 S 和 T 不相连时,S 能到达 i 代表物品 i 放入 S,i 能到达 T 代表物品 i 放入 T。当割开 S→i 的边,意味着 i 放入 T;当割开 i→T 的边,意味着 i 放入 S;当割开 u,v 之间的边,意味着 u,v 不放入同一个集合。因此最小割就是最小花费。

//SHOI 2007 善意的投票
/*n个人有两种不同的意见并且有许多朋友,需要让朋友间尽可能的统一意见(少发生冲突),如果一个人违反自己的本意也算冲突,求最少的冲突*/
typedef long long ll;
typedef pair<int,int> PII;
const ll INF = 1e18;
const int N = 5e2+7;
const int M = 2e5+7;int head[N],nex[M],ver[M],tot;
ll edge[M];
int n,m,s,t;
ll maxflow;
ll deep[N];//层级数,其实应该是level
int now[M];//当前弧优化
queue<int>q;inline void read(int &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}inline void add(int x,int y,int z){//建正边和反向边ver[tot] = y;edge[tot] = z;nex[tot] = head[x];head[x] = tot ++ ;
}inline bool bfs(){//在残量网络中构造分层图for(int i = 0; i <= n + 1; ++ i)deep[i] = INF;while(!q.empty())q.pop();q.push(s);deep[s] = 0;now[s] = head[s];//一些初始化while(!q.empty()){int x = q.front();q.pop();for(int i = head[x];~i;i = nex[i]){int y = ver[i];if(edge[i] > 0 && deep[y] == INF){//没走过且剩余容量大于0q.push(y);now[y] = head[y];//先初始化,暂时都一样deep[y] = deep[x] + 1;if(y == t)return 1;//找到了}}}return 0;
}//flow是整条增广路对最大流的贡献,rest是当前最小剩余容量,用rest去更新flow
ll dfs(int x, ll flow){//在当前分层图上增广if(x == t)return flow;ll ans = 0,k,i;for(i = now[x];~i && flow;i = nex[i]){now[x] = i;//当前弧优化(避免重复遍历从x出发的不可拓展的边)int y = ver[i];if(edge[i] > 0 && (deep[y] == deep[x] + 1)){//必须是下一层并且剩余容量大于0k = dfs(y,min(flow,edge[i]));//取最小if(!k)deep[y] = INF;//剪枝,去掉增广完毕的点edge[i] -= k;//回溯时更新edge[i ^ 1] += k;//成对变换ans += k;flow -= k;}//if(!flow)return rest;}return ans;
}void dinic(){while(bfs())maxflow += dfs(s,INF);
}int main(){scanf("%d%d", &n, &m);s = 0, t = n + 1;memset(head, -1, sizeof head);for(int i = 1;i <= n; ++ i){int x;scanf("%d", &x);if(x)add(s, i, 1),add(i, s, 0);else add(i, t, 1), add(t, i, 0);}while(m --){int x, y;scanf("%d%d", &x, &y);add(x, y, 1);add(y, x, 1);}dinic();printf("%lld\n", maxflow);return 0;
}

二、点边转化

UVA1660 电视网络 Cable TV Network

给定一张 n 个点的无向图。求最少删除多少个点,使得图不连通。

点边转化,把原来无向图中的每个点 ,拆成入点 和出点 ,则在无向图中删去一个点,等价于在网络中断开 。点边转化,把原来无向图中的每个点 x,拆成入点 x 和出点 x′,则在无向图中删去一个点,等价于在网络中断开 (x,x′)。对任意 x≠S 且 x≠T 的点 x 连有向边 (x,x′),容量为 1。对原无向图的每条边 (x,y),连有向边 (x′,y) 和 (y′,x),容量为 +∞,即防止割断。求最小割即可。

当然,在点边转化中也可以将一条边截成两半,中间插入一个点,把边的各种信息反映在这个点上。

const int N = 100, M = 5e4+7, INF = 0x3f3f3f3f;
int s1,t1,n,m;
int head[N<<1],ver[M],nex[M],edge[M],tot;
int a[N * N],b[N * N],deep[N<<1];inline void add(int x,int y,int z){//正边反边ver[++tot] = y;edge[tot] = z;nex[tot] = head[x];head[x] = tot;ver[++tot] = x;edge[tot] = 0;nex[tot] = head[y];head[y] = tot;
}inline bool bfs(){memset(deep,0,sizeof deep);queue<int>q;q.push(s1);deep[s1] = 1;//分层while(q.size()){int x = q.front();q.pop();for(int i = head[x];i;i = nex[i]){int y = ver[i],z = edge[i];//剩余容量>0才属于残量网络if(z > 0 && !deep[y]){//不只是更新deep数组,是在残量网络上更新deep数组q.push(y);deep[y] = deep[x] + 1;if(y == t1)return true;}}}return false;
}inline int dinic(int x,int flow){if(x == t1)return flow;int res = flow;for(int i = head[x];i && res;i = nex[i]){int y = ver[i],z = edge[i];if(z > 0 && (deep[y] == deep[x] + 1)){int k = dinic(y,min(res,z));if(!k)deep[y] = 0;edge[i] -= k;edge[i ^ 1] += k;res -= k;}}return flow - res;
}int main(){while(cin>>n>>m){for(int i = 0;i < m;++i){char str[20];scanf("%s",str);a[i] = b[i] = 0;int j;for(j = 1;str[j] != ',';j++)a[i] = a[i] * 10 + (str[j] - '0');for(j++;str[j] != ')';j++)b[i] = b[i] * 10 + (str[j] - '0');}int ans = INF;for (s1 = 0; s1 < n; s1++)for (t1 = 0; t1 < n; t1++)if(s1 != t1){memset(head,0,sizeof head);tot = 1;int maxflow = 0;for(int i = 0;i < n;++i){if(i == s1 || i == t1)//i是入点,i+n是出点add(i,i + n,INF);//防止被割断else add(i,i + n,1);}for(int i = 0;i < m;++i){add(a[i] + n,b[i],INF);//不能割add(b[i] + n,a[i],INF);}while(bfs()){int num;while((num = dinic(s1,INF)))maxflow += num;}ans = min(ans,maxflow);}if(n <= 1 || ans == INF)ans = n;cout<<ans<<endl;}return 0;
}

三、最小割的可行边与必须边

首先求最大流,那么最小割的可行边与必须边都必须是满流。

  • 可行边 (x,y):在残量网络中不存在 x 到 y 的路径。
  • 必须边 (x,y):在残量网络中 S 能到 x 且 y能到 T。

最小割的可行边:被某一种最小割方案包含的边。

充要条件:

1. 满流。

2. 在残余网络中找不到入点到出点的路径。

求法:

在残余网络中tarjan求SCC,入出两点在同一SCC中说明残余网络中存在入点到出点的路径。

由于该边满流,它的反向边一定存在于残余网络中,反向边与其他入点到出点的路径会构成SCC。

最小割的必须边(不考虑容量为0的边):一定在最小割中的边。

充要条件:

1. 满流。

2. 残余网络中源点能到入点,出点能到汇点。

必须边的另一种理解是扩大容量后能增大最大流的边。

求法:

在残余网络上从源点开始dfs,从汇点开始反向dfs,标记到达的点,然后枚举满流边判断即可。

若求过可行边,已经有了SCC,直接判断入点是否与源点在同一SCC中且出点是否与汇点在同一SCC中即可。

考虑为什么两种算法是等价的,由于源点到入点一定有流,所以一定存在入点到源点的有流路径,等价得证。

四、二分图的可行边和必须边

求法

  • 如果存在一组完备匹配:
    求出一组完备匹配。
    建一张新图:
    对于图上的边,若为匹配边出点向入点连边,否者入点向出点连边。
    在新图上tarjan求SCC。

可行边:此边为匹配边或入出点在同一SCC中。

必须边:此边为匹配边且入出点不在同一SCC中。

  • 如果不一定存在完备匹配:
    求出一组最大匹配。
    建一张新图:
    对于图上的边,若为匹配边出点向入点连边,否者入点向出点连边。
    加入两个点S,T。
    对于匹配的左部点u,u到S连边;右部点v,T到v连边。
    对于未匹配点的左部点u,S到u连边;右部点v,v到T连边。
    在新图上tarjan求SCC。

可行边: 此边为匹配边或入出点在同一SCC中。
必须边: 此边为匹配边且入出点不在同一SCC中。

二分图与最小割的做法本质是一样的,上述做法其实是利用二分图的性质较为简便地建出了残余网络而已。

【模板】P4126 [AHOI2009]最小割

五、平面图最小割

边与边只在顶点相交的图被称为平面图。

对于一个平面图,都有其对应的对偶图:

  • 平面图被划分出的每一个区域当作对偶图的一个点;
  • 平面图中的每一条边两边的区域对应的点用边相连,特别地,若两边为同区域则加一条回边(自环)。

这样构成的图即为原平面图的对偶图。

平面图最小割等于对偶图最短路。

【模板】P4001 [ICPC-Beijing 2006]狼抓兔子

六、最小割的一些小技巧

1.记录划分方案

我们可以通过从源点 S 开始 DFS ,每次走残量大于 0 的边,找到所有 S 点集内的点。

void dfs(int u) {vis[u] = 1;for (int i = lnk[u]; i; i = nxt[i]) {int v = ter[i];if (!vis[v] && val[i]) dfs(v);}
}

T点集合同理。

2.求割边数量

只需要将每条边的容量变为 1 ,然后重新跑 DinicDinicDinic 即可。

3.求最小边的最小割

设总边数为E,跑最大流之前所有的边权都乘E+1然后再+1

得到的结果应该是mincut∗(E+1)+割边数量(这个比较显然吧)

由于割边数越小,跑出来结果越小,所以就自动选了割边数量小的边(但相同不能保证字典序)

结果就是最小边数

E+1也可以替换成大于边数的数。

4.输出任意一种最小割的方案

跑过一次最大流之后,在残量网络上,s和t之间不连通了
进行一次dfs/bfs,求出从s出发能到达的点集S,和不能到达的点集T
所有从S跨越到T的满流边(残留网络为0)构成了一组最小割

5.判断一条边是否满流

运行一次最大流算法,得到一个残量网络
取残量网络上的一条满流边(u, v),判断这条边是否一定满流
对残量网络运行Tarjan算法,求出所有SCC
当u和v不属于同一个SCC的时候,这条边一定满流
否则,我们可以在SCC中找到一个包含这条边的反向边的环,沿着环增广一次,仍然不破坏流量平衡,但是这条边已经不满流了

6.判断某一条边是否可能为最小割中的一条

所有一定满流的边都可能为最小割

7.判断某一条边是否为最小割中的一条

我们考虑一条边是否一定在最小割中。明显,我们跑完一遍网络流之后残量网络里满流的边都是可能在最小割里的,然后又到了跑Tarjan的时候了,如果这条边在最小割里,那么跑完Tarjan之后它所连接的两个端点一定是在不同的强连通分量里。

8.判断某条边是否一定出现在最小割中

首先还是对残量网络求SCC
考虑一条满流边(u, v),判断她是否一定出现在最小割中
当u和s属于同一个SCC,并且v和t属于同一个SCC的时候,这条边一定出现在最小割中

参考:

  • 《最小割模型在信息学竞赛中的应用》
  • 最小割总结

模板 - 最小割(常见最小割题型技巧总结)相关推荐

  1. 【网络流24题】B、太空飞行计划问题(最大权闭合图转最小割、最小割方案输出)

    整理的算法模板合集: ACM模板 B.太空飞行计划问题(最大权闭合图转最小割.最小割方案输出)[省选/NOI- ] P2762 太空飞行计划问题 [问题分析] 最大权闭合图问题,可以转化成最小割问题, ...

  2. [学习笔记]最小割之最小点权覆盖最大点权独立集

    最小点权覆盖 给出一个二分图,每个点有一个非负点权 要求选出一些点构成一个覆盖,问点权最小是多少 建模: S到左部点,容量为点权 右部点到T,容量为点权 左部点到右部点的边,容量inf 求最小割即可. ...

  3. 【每日算法】【图论】【最小边覆盖 最小路径覆盖 最小顶点覆盖 最大独立集 最大团】

    最小边覆盖 = 最大独立集 = |V| - 最大匹配数 这个是在原图是二分图上进行的 最小路径覆盖和最小边覆盖不同,不要求给的图是二分图,而是要求是N x N的有向图,不能有环,然后根据原图构造二分图 ...

  4. 计算机电源故障维修方法,常见电源故障维修技巧

    常见电源故障维修技巧 计算机电源也会经常发生故障,那么怎么去解决呢?下面是关于常见电源故障维修技巧,欢迎阅读! 一.故障现象: 开机电源指示灯不亮. 检查内容: 电源是否接好,闸刀是否闭合,如果是三相 ...

  5. abaqus python二次开发攻略_Abaqus有限元分析常见问题解答与实用技巧 12天后上架...

    Abaqus有限元分析常见问题解答与实用技巧已印刷完毕,1-2天后上架,先睹为快.现在某些网站上的售卖信息,不靠谱.温馨提示:封底无防伪标均为盗版! 序  言 Abaqus是是国际上先进的大型通用非线 ...

  6. 树形DP求树的最小支配集,最小点覆盖,最大独立集

    转自:https://www.cnblogs.com/Ash-ly/p/5783877.html 一:最小支配集 考虑最小支配集,每个点有两种状态,即属于支配集合或者不属于支配集合,其中不属于支配集合 ...

  7. Bayes决策理论(一):最小错误率和最小风险决策

    Bayes决策理论(一):最小错误率和最小风险决策 Bayes定理 符号:wi 表示类别:x表示待分类的样本或者其特征:p(wi)为类别的先验概率,表示每个类别在所有类别中出现的概率:p(x|wi)为 ...

  8. 最小堆实现最小优先队列

    最小优先队列的可以实现的操作: insert,插入一个元素 min,找到最小的元素 extract-min,去掉最小的元素并返回最小的元素 increase-key,增加一个关键字的值 优先队列的形式 ...

  9. 最小堆以及最小优先队列的实现

    最小堆的实现 什么是最小堆 构建最小堆 MIN_HEAPITY的实现 MIN_HEAPIFY的时间复杂度分析 BUILD_MIN_HEAP的实现 建最小堆的时间复杂度分析 什么是最小堆 最小堆从逻辑上 ...

最新文章

  1. jquery如何判断div是否隐藏
  2. time、deltaTime、fixedTime、fixedDeltatime的区别
  3. 牛客竞赛语法入门班数组字符串习题【完结】
  4. python中range和xrange的区别_python中range和xrange的区别
  5. Ubuntu18 搭建apache2+php5.6+mysql服务器
  6. 2010中国杭州电子信息博览会通讯报道
  7. Linux服务-telnet服务部署(配置允许root登录)
  8. import导入模块
  9. 很基本的权限功能小结
  10. python应用实例论文_浅谈Python在科学计算中的应用
  11. java线程创建过程_Java创建线程的细节分析
  12. [Oracle] SQL*Loader 详细使用教程(3)- 控制文件
  13. hortonworks/registry 的Registry,registry存在,但是却查不到
  14. 使用HTML5创建和播放声音
  15. [翻译]Real-Time Correlative Scan Matching
  16. lIUNX如何加载U盘,光盘
  17. 常见的SQL面试题:经典50例
  18. IP,路由器工作原理、MAC,交换机工作原理、CSMA\CD、令牌环网
  19. 2018年博客之星,需要您宝贵的一票!
  20. MySQL性能瓶颈排查

热门文章

  1. hbase各个角色的分工
  2. 电压放大器在压电陶瓷损伤识中的应用
  3. 无人驾驶运动控制(二):模型预测控制
  4. 对抗攻击8——CW(Carlini Wagner)
  5. BCM68385方案产品静电问题分析
  6. vivo的android版本功能介绍,vivoX30发布,全新版本OS,新功能应用分享登场
  7. Vue全家桶有哪些?(详细)
  8. html打印预览首行缩进样式无效,css首行缩进没有效果的原因及解决办法
  9. char/nchar/varchar/nvarchar/varchar2之间如何选择使用?
  10. 计算机,英语,人文书籍廉价大甩卖,有买有送