本文主要是介绍网络流模板 网络流题型大荟萃,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
以HDU4560为例,整理了很多网络流的题目——
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a, T b) { if (b>a)a = b; }
template <class T> inline void gmin(T &a, T b) { if (b<a)a = b; }
const int N = 240, M = 14000, Z = 1e9 + 7, inf = 0x3f3f3f3f;
int casenum, casei;
int n, m, g, L;
bool e[80][80];
/*
【算法介绍】
网络流常用于解决分配匹配等问题。
其主要算法包括dinic和sap(isap)
其中,Dinic是基于层次图的网络流模型,时间复杂度为O(n ^ 2 * m)【算法实现】
1,先通过bfs,在有流量的条件下,找到从超级源点ST到超级汇点ED的最短路
2,再通过dfs,在确保是最短路的条件下,找到一条可行的增广路
3,重复过程(1,2),直到过程1无法找到可行路径
注意:
1,网络流的实现,必然要依赖反图和反边,id = 1的初始化莫不可忘记。
2,每条单向边都对应着正边和反边,整个图的最大边数是所有单向边的数量 * 2
3,我们初始化的时候,一定要使得所有first都清零,而不只是1 ~ n的first清零
4,建图是可以慢慢化简的,如果想不到好的建图方式,可以先想拙劣建图法再分析化简【最小割】
在网络流模型中,最大流 = 最小割。
所谓最小割, 是指用最小的成本,使得源点和汇点不连通。
最小割模型有两种可能的答案生成方式——
1,最大流就是答案
2,所有的正权,减去最大流才是答案
我们可以对对最小割生成方案,用最小割解决一些模型,或者利用最小割的思想解决问题。【最大权闭合子图】
所谓最大权闭合子图,大概有这样的问题结构——
1,告诉你每个点的权值(权值有正有负)
2,点与点之间的选取,存在一定的依赖关系。
就比如说如果选了x,则必须要选择另外一些点{p1, p2, p3},即{px}是x的必要条件。
3,你需要决定选出若干点,进而使得点权之和最大
建图方式:
1,源点->正权点,容量为权值绝对值
2,负权点->汇点,容量为权值绝对值
3,对于如果选了x,就必须要选择y的情况,我们从x向y建立一条容量为inf的边
在这种情况下,我们跑最大流,得到最小割
与ST相连的点属于最大权闭合子图中的点,与ED相连的点不属于最大权闭合子图。
理解:
最大流 = 最小割,而最小割,意味着以最小成本,使得源点与汇点不连通。
在这种建图方式下——
割掉ST->正权点的边,意味着记下来所对应的成本太大了,我们不如放弃该正权
割掉负权点->ED的边,意味着我们虽然使用了这些成本,但是正权还没有被画完
而依赖关系之前的正无穷的边, 意味着这前驱节点和后继节点之间是不可同时割舍的
总的正权值 - 最小割, 就是问题的解【混合图欧拉回路】
网络流可以解决混合图求欧拉回路的问题
混合图欧拉回路问题大概是这样:告诉你一个图,有些边方向是确定的,有些边方向是不确定的(需要你定向)。
要求你在定向之后,使得该图变为欧拉图。
首先我们把双向边任意定向,并对于每个点,统计其在当前定向条件下的入度和出度。
如果对于任意点,(入度 - 出度) % 2 == 1,则无解。
否则我们设(ind[i] - oud[i]) / 2 = w
对于入度>出度的点(即w > 0),我们从这个点向ED连容量为abs(w)的边使之平衡化
对于出度>入度的点(即w < 0),我们从ST向这个点连容量为abs(w)的边使之平衡化
然后跑最大流。如果最大流为∑{w, w > 0},则表示问题有解。
理解:
对于入度 > 出度的点,其向ED连了容量为abs(w)的边都要流满
如果流满了,流出的容量为出度+w = 入度-w,也就是说,我们改变1/2的入度的方向即可
对于入度 < 出度的点,其从ST连了容量为abs(w)的边都要流满
如果流满了,流入的容量为入度+w = 出度-w,也就是说,我们改变1/2的出度的方向即可
于是这种建图能对应得到问题的解。
如果要输出方案,我们把所有从x到y没有流量的边反向即可
该边流满,说明我们选择了这条边;该边未满,说明这条边需要反向
*/
int ST, ED;
int first[N], ID;
struct Edge
{int w, cap, nxt;
}edge[M];
void ins(int x, int y, int cap_)
{edge[++ID] = { y, cap_, first[x] };first[x] = ID;edge[++ID] = { x, 0, first[y] };first[y] = ID;
}
int d[N];
bool bfs()
{MS(d, -1);queue<int>q; q.push(ST); d[ST] = 0;while (!q.empty()){int x = q.front(); q.pop();for (int z = first[x]; z; z = edge[z].nxt)if (edge[z].cap){int y = edge[z].w;if (d[y] == -1){d[y] = d[x] + 1;q.push(y);if (y == ED)return 1;}}}return 0;
}
int dfs(int x, int all)
{if (x == ED)return all;int use = 0;for (int z = first[x]; z; z = edge[z].nxt)if (edge[z].cap){int y = edge[z].w;if (d[y] == d[x] + 1){int tmp = dfs(y, min(edge[z].cap, all - use));edge[z].cap -= tmp;edge[z ^ 1].cap += tmp;use += tmp;if (use == all)break;}}if (use == 0)d[x] = -1;return use;
}
int dinic()
{int ret = 0;while (bfs())ret += dfs(ST, inf);return ret;
}
void build(int mid)
{MS(first, 0); ID = 1;for (int i = 1; i <= m; i++)ins(n + i, n + m + i, L);for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){if (e[i][j])ins(i, n + m + j, 1);else ins(i, n + j, 1);}}ST = 0;ED = n + m + m + 1;for (int i = 1; i <= n; i++)ins(ST, i, mid);for (int i = 1; i <= m; i++)ins(n + m + i, ED, mid);
}
void debug()
{for (int x = 0; x <= ED; ++x){for (int z = first[x]; z; z = nxt[z])if (z % 2 == 0)//正边{int y = w[z];if(cap[z ^ 1])printf("%d->%d(%d)\n", x, y, cap[z ^ 1]);}puts("");}
}
int main()
{scanf("%d", &casenum);for (casei = 1; casei <= casenum; casei++){scanf("%d%d%d%d", &n, &m, &g, &L);MS(e, 0);while (g--){int x, y; scanf("%d%d", &x, &y);e[x][y] = 1;}int l = 0;int r = m;while (l < r){int mid = (l + r + 1) >> 1;build(mid);if (dinic() == mid * n)l = mid;else r = mid - 1;}printf("Case %d: %d\n", casei, l);}return 0;
}
/*
[题意]
本题为HDU4560
n个歌手,m种歌曲流派(n<=m<=75)
我们想要安排尽可能多的演唱会。不过有以下条件——
1,每场演唱会中,每个歌手要唱不同类型的歌曲。
2,这样可能导致有些歌手去唱他不擅长的歌曲。对于任一种歌曲,被不合适唱的次数都不能超过L。
问你最多能安排多少场演唱会。
[做法]
首先我们把烦琐的问题信息提取出来。
本题中出现了三类信息——歌手、歌曲、演唱会
我们没办法通过流量关系,使得一个演唱会在收集了n个不同的歌曲之后,向汇点贡献1的收益。
我们研究一下性质,发现,如果我们二分了演唱会的场次(显然满足单调性,可以二分)
我们只要使得每首歌被最多演唱该场次次就好了,即我们把演唱会由点转化为了二分的容量上限。
然而,对于每个歌曲而言,其有擅长演奏和不擅长演奏两种关系,被不擅长演奏的次数不能超过L
这里考虑把第i首歌拆点为i和m+i,之间的容量上限为L,i表示不擅长的,m+i表示擅长的
然后每个歌手向其擅长或不擅长的点连容量上限为1的边。
这样跑最大流,如果最大流 = 歌手数 * 演唱会数,那说明二分成功。由此得到答案POJ1149
[题意]
有 M 个猪圈,每个猪圈里初始时有若干头猪。一开始所有猪圈都是关闭的。
依次来了 N 个顾客,每个顾客分别会打开指定的几个猪圈,从中买若干头猪。
每个顾客分别都有他能够买的数量的上限。
每个顾客走后,他打开的那些猪圈中的猪,都可以被任意地调换到其它开着的猪圈里,然后所有猪圈重新关上。
问总共最多能卖出多少头猪。( 1 <= N <= 100, 1 <= M <= 1000)
举个例子来说。有 3 个猪圈,初始时分别有 3、 1 和 10 头猪。
依次来了 3 个顾客,第一个打开 1 号和 2 号猪圈,最多买 2 头;
第二个打开 1 号和 3 号猪圈,最多买3 头;
第三个打开 2 号猪圈,最多买 6 头。
那么,最好的可能性之一就是第一个顾客从 1 号圈买 2 头,然后把 1 号圈剩下的 1 头放到 2 号圈;
第二个顾客从 3号圈买 3 头;
第三个顾客从 2 号圈买 2 头。总共卖出 2+3+2=7 头。
[分析]
有 M 个猪圈,每个猪圈里初始时有若干头猪。一开始所有猪圈都是关闭的。
依次来了 N 个顾客,每个顾客分别会打开指定的几个猪圈,从中买若干头猪。
每个顾客分别都有他能够买的数量的上限。
每个顾客走后,他打开的那些猪圈中的猪,都可以被任意地调换到其它开着的猪圈里,然后所有猪圈重新关上。
问总共最多能卖出多少头猪。( 1 <= N <= 100, 1 <= M <= 1000)
举个例子来说。有 3 个猪圈,初始时分别有 3、 1 和 10 头猪。
依次来了 3 个顾客,第一个打开 1 号和 2 号猪圈,最多买 2 头;
第二个打开 1 号和 3 号猪圈,最多买3 头;
第三个打开 2 号猪圈,最多买 6 头。
那么,最好的可能性之一就是第一个顾客从 1 号圈买 2 头,然后把 1 号圈剩下的 1 头放到 2 号圈;
第二个顾客从 3号圈买 3 头;
第三个顾客从 2 号圈买 2 头。总共卖出 2+3+2=7 头。2016计蒜之道复赛F
[题意]
https://nanti.jisuanke.com/t/11215
n(100)个点和m(C(n,2))条双向边
有询问st,md,ed(三点各不相同)
问你是否存在一条合法路径,使得我们从st到md,再从md到ed,同一个点最多只能经过一次。
[分析]
我们以md为源点建图,在st和ed处都各自向汇点连一条容量为1的边,看最大流是否为2
但是这里因为每个点最多只能经过一次,所以我们需要做拆点处理
然而因为最多只增广两次,虽然理论复杂度是O(nnm),但是实际复杂度却只有O(n+m)hihocoder1389 ACM-ICPC国际大学生程序设计竞赛北京赛区(2016)网络赛 G
[题意]
有n(100)个工厂,会制造垃圾。
有m(100)个垃圾处理厂,可以处理垃圾
每个工厂有属性(纵坐标,横坐标,日排放垃圾量)
每个垃圾处理厂有属性(纵坐标,横坐标)
对于所有垃圾处理厂,其可以设置它的处理能力w和处理半径d(统一设置)
成本就是w*sqrt(d),让你用最小的成本处理完所有工厂的垃圾
[分析]
这道题有两个参数需要我们设置,处理半径d和处理能力w
对于处理半径,并没有什么单调性
但是处理半径最多也不过有n*m种可能
于是我们可以处理出所有的处理半径,并做去重(优化1,去重),
然后按照半径从大到小的顺序,枚举所有可行的处理半径(优化2:该处理半径必要能把所有工厂都覆盖)
在确定了处理半径之后,
我们二分每个垃圾处理厂的处理能力,其二分下界显然是Basic,
而二分上界则通过之前答案做最优性剪枝(即min(TOP, (int)(ans / sqrt(sqrt(d) + 1)));)
每次跑dinic来验证该参数设置的合法性
具体的建图方式很简单——
源点->工厂(垃圾生产量)
工厂->能处理其的垃圾处理厂(无穷大 or whatever)
垃圾处理厂->汇点(工厂处理能力)HDU3572
[题意]
有m(200)个机器,n(500)个任务
每台机器每天只能进行一项任务,但是每个任务可以在不同的天打断或者在不同的机器上执行
每个任务有开始时间,持续时间,结束时间,时间范围不超过500
现在询问是否存在一个可行的方案,使得按时完成所有任务。
[分析]
有三类信息——机器、任务、时间
源点向每个任务连容量为其完成需要时间的边
每个任务向其所在的时间段内的每天连容量为1的边
每个时间向各个机器都连容量为1的边,每台机器向汇点连容量为天数(或者极大值)的边
但是这里我们可以优化一下——每个时间向汇点连容量为机器数的边,然后跑最大流HDU2883
[题意]
n(200)个人来买蛋糕
商店同时可以做m(1000)个蛋糕
对于第i个人,要求蛋糕最早在si做,最晚在ei做(时间范围为[1,1e6]),需要买ni(50)块蛋糕,每块蛋糕要求烹饪ti(50)时间
问你是否可以满足所有人的需要
[分析]
这道题与HDU3572十分类似,ni完全可以*=ti表示第i个人所需要的时间
然而差异是,我们没办法把时间点逐一抽象为具体点
但是我们发现时间区间段最多不过400段,于是我们可以对时间区间段做离散化
超级源点向人连边,容量为人需要做蛋糕的总时间
人向时间区间段连边,容量为在这个区间段能够做这些蛋糕的时间,即(a[j+1]-a[j])*g[i]
(这里应该用到蛋糕的数量,使得同一个蛋糕不能同时在多个机器上烤。但是本题的设计却要忽略这个)
从时间区间段向超级汇点连边,流量为时间长度*mHDU2732
[题意]
有一个n*m的矩阵,每个点是一根柱子,每根柱子有一个最大的承受跳跃的次数,就是允许蜥蜴跳上去的次数
所有蜥蜴最大跳跃的欧几里得距离告诉你
所有蜥蜴所在的位置也告诉你
所有柱子的信息也告诉你
问你是否存在一种方案,使得所有的蜥蜴都跳出这个矩阵,注意同一时间同一根柱子上最多只能有一只蜥蜴
[分析]
建图方式——
超级源点->有蜥蜴的柱子(1)
每根柱子拆点,入->出(承重次数)
出->其他能跳到的柱子or终点(inf)HDU3338
[题意]
给定一个n*m的方格,
这些格子有点格子是需要填数的,另外一些格子有记录关于行或列的值。
我们需要填数,使得所有记录值都满足。
[分析]
这里要要求每个点的流量需要是在[1~9]之间,即流量产生了下界。
这时上下界网络流的问题,我们可以通过上下界网络流的方法解决。
当然还有其他方法,我们把下界 -= 1,使得流量消除掉下界的影响。
这里既要行满足又要列满足,于是我们按照——
列 -> 点 -> 行的方式建边,查看能否跑出正确的最大流即可Codeforces Round #304 (Div. 2)E
[题意]
http://codeforces.com/contest/546/problem/C
有n(100)个点m(200)条边
一开始n个点上分别有a[i]个人
我们希望最后n个点上分别有b[i]个人
每个人可以不动或者最多通过一条边走一次
[分析]
这道题我一开始想到的是费用流——
拆点是很必要的,每个点拆成两个点x, y。
源点流向x,流量为a[i],费用为0,
x流向自己的y和其他点的y,流量为无限大,费用为1,
y流向汇点,流量为b[i],费用为0,
如果最大流 = n且费用 = n即可以。
然而这道题我们还可以通过最大流解决——
因为这里费用为1的条件实际上是并没有什么卵用的。HDU5352
[题意]
有n个城市,m个时间点,再给定一个K(n<=200,m<=500,k<=200)
这n个城市一开始都是废墟
对于每个时间点有3个操作
1 x:自己选择重建若干个城市(可以为0~K的任一值),这些城市必须与x相连通(包括x)
2 x y:在x和y之间建一条双向边。
3 p (x1 y1)(x2 y2)(...)(xp yp):删掉这p条边。
我们希望在m个时间点结束之后,重建城市的数量尽可能多。
在满足重建城市尽可能多的基础上,我们希望使得{每个时间点修建城市数}这个序列的字典序尽可能小。
[分析]
首先,这是关于分配的问题——
关于分配的问题,我们想到用网络流来求解
我们是把时间点与城市之间分配
所以,建图就是——
超级源点向所有时间点连边,每个时间点向其所可以建的城市连边。
然后,如果不考虑字典序的话,这道题已经结束了,跑最大流就是答案
可以如果考虑字典序的话,我们就要使得倒着分配到尽可能多的的城市,即倒着跑最大流
靠后的时间点分配的城市多,那么靠前的时间点分配的城市自然就少了
这个是满足网络流的参量网络增广思想,分配可能会变,但最大流量是不会变的。
因为已经有时间点了,所以我们我们并不需要记录所有询问再倒着处理。
只需要在倒序处理的时候,在所有操作1的位置,使得源点多增加K条边的容量上限就好啦2015HDU Summer Trainning(4) - Team 10J
[题意]
给你一个n * m的棋盘(n和m都在40的范围内)
每次选择两个相邻的格子,两个数都+1,问最少多少次操作可以使得棋盘上的所有数都变成同一个数。如果无法实现输出-1
[分析]
这道题的点权很大,但是点数并不多。
我们发现这题有奇偶点的性质,每次是奇点和偶点权值共同+1 ★这个性质十分重要★
1,如果格子数为偶数,
最终偶点的权值和与奇点的权值和是相同的,所以要求初始先统计出奇点的权值和和偶点的权值和,两者必须相同,否则直接判-1
我们有操作的最小次数(设为bot),显然也会有多一些的次数。
我们发现,多的次数是可以在小次数的基础上发展而来的。
那么达成bot,bot+1,bot+2,……,都是可行的,满足单调性,可以二分。
2,如果格子数为奇数(即偶点比奇点多1),结合★的性质,我们发现合法的目标是唯一的。
问题一——为什么呢?
因为在bot的基础上,所有点都抬升v,显然偶点权值和会比奇点权值和多v,肯定无法达成。
问题二——如何计算呢?
设目标权值是x,
设偶点的个数为num0,偶点初始的权值和为v0
设奇点的个数为num1,奇点初始的权值和为v1
我们可以算得偶点一共加了多少次,奇点一共加了多少次,两者必须相同。
那么所有偶点的权值增加量为num0*x-v0
所有奇点的权值增加量为num1*x-v1
必然有num0*x-v0=num1*x-v1
只有x是未知量,x=(v0-v1)/(num0-num1),即x=v0-v1;
这样设置一个超级源点,向所有偶点连边,容量为目标权值-初始权值
设置一个超级汇点,所有奇点向超级汇点连边,容量为目标权值-初始权值。
所有奇点向周围的所有偶点连边,容量为无穷大。
这样跑一个最大流,如果最后,最大流=∑每个点的目标权值-初始权值,这个目标权值就是合法的。Codeforces Round 290 (Div 2)E
[题意]
有n个数,每个数的范围都在[2,10000]之间。
我们希望把这些数安排在若干个环上,
使得每个环,数的个数都>=3,且所有相邻的数之和都为素数。
[分析]
这题有很多地方是可以深入分析的——
1,首先两个数的和是质数,每个数都是[2,10000]范围内的数,所以和为素数的2个数必定是一奇一偶
2,然后因为最后所成是圆桌,所以点数必定为偶数,
3,每桌人数至少为3,这个限定有什么意义呢?只是断绝了2个人一桌的可能性。
也就是说,两个人的相邻关系,最多只有1个,不能double化。
奇偶性就已经让我们想到网络流了。
而不能double化也向我们发现这个恰好契合于流量的性质。
于是我们可以——
把所有奇点向"与它之和为素数的"偶点连边,方向是奇->偶,容量是1,
然后所有奇点从超级源点收入容量为2的边
所有偶点至超级汇点流出容量为2的边
然后跑最大流。
因为题目要求其实就是——
1,每个奇点恰好与2个偶点匹配,
2,每个偶点恰好与2个奇点匹配。
所以如果最后的流量恰好为n,就是有可行方案的。HDU5594 BestCoder Round 65E n个数形成若干至少3元素素数环的可行性检验 数可以为1
[题意]
这道题是 CF290(Div2)E的升级版。
不同的是,这道题的数值范围包括了1.
如果不包括1,问题会怎么样?
显然,素数不可能是2,只会是奇数。
于是,相邻两个数必定是一奇一偶。
于是,每个环中数的个数也必定为偶数个。
(题目中也必须要使得奇数和偶数的个数都相同)
然后,我们把所有奇数向与之之和为素数的偶数之间连一条流量为1的边
超级源点向所有奇数连一条流量为2的边
所有奇数向超级汇点连一条流量为2的边
然后如果最大流==点数n,那么说明有解。
这样建图,每个奇数必然会匹配到2个偶数,
于是自然使得每个组中,数的个数都必然至少为4个,保证了做法天衣无缝。
===============================================================
问题回归,现在数可以为1了。
会产生什么影响呢?
如果只有1个1,是没有任何影响的。
如果超过1个1,两个1之间是可以形成一个素数的,即,会出现奇数向奇数连边的情况。
而且,其实对我们产生干扰的,也只有1向1连边这一种情况。
(情况1)如果这些1不形成环,只是链关系的话,
我们可以考虑移除1~n-1个1,设剩余数的个数为m,我们需要查看,是否剩下的数可以跑出流量为m的最大流。
至于我么移除的1,随便和剩下的哪个1相邻即可。(也就是有一种——1个1可以拆出多个1的意味。)
这个肯定不会把是YES的情况判定为NO。这种做法下,1的个数需要至少为2个。
(情况2)然而,如果1的个数如果是大于等于3,我们是可以把所有的1都移除的。因为这些1可以自成一环。
以上做法的思想是什么呢?
就是因为发现1可以与自己相连。所以采取的一种——
"如果1多了,以至于出现1和1连边的情况了,那么我们试着减少1的数量"的做法。
(情况3)然而,这个量的减少,有时候会产生错误——
因为1的减少,我们可能一个环中只剩下2个数了。
如果有这么的一个情况出现,这种环的个数,也必然只有1个。否则2个环可以拼起来。
于是,我们缩1,不能盲目缩,我们可以缩出special one。
什么叫做special one呢?
就是这个1,连向的sum=prime的偶数的边的流量,由1变成了2。
这个解决了情况3出现的影响。也不会产生任何非法干扰。于是就可以AC掉这题啦!
这题我会不断删点,不断做网络流。
然而流量是不断减少的,于是我们每次都要重置边的流量。
所以,我们可以使得一开始从流量最少的情况入手,不断加边。这样就不会重置边,可以有加速的奇效。
然而,我们还发现,在经过我们的缩1操作处理之后。
实际上,奇数的个数还是要和偶数个数保持相同。
因为偶数个数是不变的。所以,事实上只需要对一个值跑网络流即可。
啦啦啦啦啦~Codeforces Round 284 (Div 2)E
[题意]
有n(100)个数a[]
有m(100)个pair,每个pair的两个数必然一奇一偶
我们每次操作可以选择一个pair,和一个值v,要求v是这个两个数的公约数,使得这个pair的两个数都/=v
问你最多的操作次数。
题目保证pair两两不同。
[分析]
首先,这道题设计到奇偶性质,我们很自然就想到了网络流。
然后,基于贪心原则,我们发现v必然一定取素数。
素数不同条件下的建边不能相互影响,于是我们可以拆素因子,
即奇数x->奇数x的素因子p1(num1)->与x可以匹配的偶数y的素因子p1->x可以连边的偶数y(numy)
->奇数x的素因子p2(num2)->与x可以匹配的偶数z的素因子p2->可以连边的偶数z(numz)
这种限制可以使得对于每个数,素因子的统计不会过量,且使得匹配关系可以对应实现
不过因为不同素因子之间相互独立,所以我们还可以枚举素因子,每次枚举素因子之后单独跑一次dinic——
1,对于每个奇数,数中含有几个v,就从超级源点向其建立容量为几的边
2,对于每个偶数,数中含有几个v,就从其向超级汇点建立容量为几的边
3,对于一个(奇数,偶数)pair,建一条边,容量为它们共有的v的数量。
求和就是答案IndiaHacks 2016 - Online Edition (Div 1 + Div 2) ErrichtoD Delivery Bears
[题意]
n(50)个点,m(500)条有向边,我们有g(1e5)只运输熊。
每只熊都必须运输同样重要的货物(重量可以为double),从1点到n点。
问你每只熊最多可以运多少的货物
[分析]
如何把我们转化成——每只熊都运同样重量的货物呢?
我们很快就可以发现,可以二分每只熊运送的货物w,
然后对于每条边,用a[i].z/w来算出这条边的容量。
然后跑最大流检测是否>=g【最小割模型】重要问题,输出方案
[题意]
找出流网络G=(V,E)的一组最小割的边集
[做法]
1,求出最大流,得到残量网络
2,从源点开始,顺着未满流的边做bfs,得到的连通点集表示未与源点割掉的点集
3,枚举所有与源点相连的点x,再枚举这些点的所有边得到另一侧的点y。如果x与y之间的边是满流的边,则说明这是一条割边。HDU5294_2015MUTC第一场7G 基于最短路的最少边数和最小割
[题意]
给你一个无向图
从1到n会有一个最短路
问你
(1)最少切除多少条边, 可以使得最短路变长
(2)最多切除多少条边, 可以使得最短路依然会这个长度
[分析]
首先肯定要处理出最短路
对于(2),除了最短边数的最短路的这些边外,我们把其他边都删掉
对于(1),我们是对最短路图做最小割,在dinic()的bfs和dfs中,更新的条件都要同时满足d[y]=d[x]+1&&f[y]=f[x]+c[z]
如果是双向建边,我们可能在奇怪的地方连出环会出错,所以一定不要双向建图,最短路是有方向的。HDU3657
[题意]
给你一个n*m(50*50)的矩阵
每个格子可以选择 取或不取
如果取,我们会获得其相应权值
但是如果相邻的两个格子xy都同时取,我们会损失2*(x&y)的权值
同时还有一些格子必须取
让你输出我们所能够得到的最大权值
[分析]
这涉及到决策,我们考虑网络流,而这里是选取问题,我们可以考虑最小割模型。
最小割模型有两种可能的答案生成方式——
1,最大流就是答案
2,所有的正权,减去最大流才是答案
这道题按照第二种方式建图——
割掉的边表示我们需要舍弃的成本,因为是最小割,所以是我们以最小的损失使得问题合法化。
1,所有的正权tot = ∑a[][]
2,这个图其实是二分图,我们应该想到奇偶建图方式
从奇点向偶点连边,边权为这两个点同时选时会造成的损失,
3,一个点的选取,并不一定使得这个点与源点相连,如果一个点必须选
奇点的话与源点的边权为极大值,偶点的话与汇点的边权为极大值
以这种方式建图之后——
如果一个点不选取,这个点会与源点或汇点形成割,也就不用考虑其它问题了。
如果一个点选取,而且其相邻的点也选取,则我们会计入这份共同选择的损失割掉。
于是,这个模型下, tot - dinic() 就是答案HDU4307
[题目]
有一个1*n的矩阵A(我们需要求A),矩阵中的每个元素不是0就是1
给你一个n*n的矩阵B(已知),矩阵中的每个元素都是非负整数
给你一个1*n的矩阵C(已知),矩阵中的每个元素都是非负整数
有这样的关系方程:
D=(A*B-C)*AT(AT是A的转置矩阵,a[i][j]->a[j][i])
让我们(构造这样的A从而)使得D最大。
[分析]
太神奇了!
我们用最大流可以求解一个函数模型的最小值——
for(int i=1;i<=n;++i)
{sum+=x[i]*a[i];sum+=(1-x[i])*b[i];for(int j=1;j<=n;++j){sum+=x[i]*(1-x[j])*c[i][j]; }
}
其中x[i]不是0就是1。
意味着,我有n个开关,每个开关控制一个决策不是0就是1.
如果对于开关i:值为1,就会损失a[i]的价值;值为0,可会损失b[i]的价值,
且对于开关对(i,j),i为1且j为0就会损失c[i][j]的价值。
如何对这个模型用最大流建模呢?
我们产生一个假设,假设做完最小割之后——
值为1的集合是和ST相连的部分,值为0的集合是和ED相连的部分
建图方式——
ST->点i(i为0的损失量),点i->ED(i为1的损失量) 即这两条边的连边是反着来的
点i->点j(i为1且j为0的损失量)
在这种建图方式下,我们通过跑最大流的方式得到最小割。
因为是跑最大流,所以这两条边必然会至少割掉一条,也就是说,我们确实能够对每个点作以分配。
要不与ST之间割掉,即i为0的损失小,我们割掉这条与ST相连的边,表示i的值选取了0
要不与ED之间割掉,即i为1的损失小,我们割掉这条与ED相连的边,表示i的值选取了1
我们研究任意一条路径
ST->u->v->ED
ST->u的容量为u为0的损失
v->ED的容量为v为1的损失
u->v的容量为u为1v为0的价值
显然——
我们要不u选0(割掉ST-u),损失一些价值,就不会再产生u选1其他点选0的损失了
我们要不v选1(割掉v-ED),损失一些价值,就不会再产生u选0其他点选1的损失了
我们要不u选1(不割掉ST-u),v选0(不割掉v-ED),这2者都不割掉。
但是我们就要相应割掉(u->v),因为我们势必要损失这里的价值
==========================================================================
知道了具体函数模型,再回归该题而言,
我们对D=(A*B-C)*AT做化简
ans=0;
for(int i=1;i<=n;++i)
{for(int j=1;j<=n;++j){ans+=a[i]*a[j]*b[j][i];}ans-=c[i]*a[i];
}
如果a[i]==1,我们会损失c[i]的收益
如果a[i]==1,a[j]==1,我们会获得b[j][i]的收益
还不够,我们再把问题转化一下——
ans=∑(b[i][j]);
<<st
for(int i=1;i<=n;++i)
{if(a[i] == 1) ans += c[i];else if(a[i] == 0) ans += ∑b[i][j=1~n] (0的话删掉整行)if(a[i] == 1 && a[j] == 0)ans += b[i][j] (1的话还删掉一些同行的格子)
}
end>>
dinic确实是[st end]的最小值,ans-= dinic() 就确实是原始函数的最大值啦TOJ3864
[题目]
http://acm.tju.edu.cn/toj/showp3864.html
有两个城市
有n(100)个将军
每一对将军i和j,如果在同一个城市的话得到lij的杀伤力(l[i][j]=l[j][i])
但是移动一个将军i的位置会丧失mi的的杀伤力
现在知道每个将军初始所在的城市
问你最多能得到多少的杀伤力
[分析]
如果i在1,j在0,损失为l[i][j]
如果i在0,损失为mv*(i==1)
如果i在1,损失为mv*(i==0)
我们想使得总损失尽量小。
建图——
ST(1)->i(i初始在1,最后去0会损失mv;如果这个损失比较小,我们会选择割掉另外一条边,即割掉ST->i的容量为mv)
i->ED(0)(i初始在0,最后去1会损失mv,如果这个损失比较小,我们会选择割掉另外一条边,即割掉i->ED的容量为mv)
i->j(i在1且j在0的损失量l[i][j])
tot-dinic()就是答案hihocoder1252 2015北京赛区D
[题意]
给你一个有向无环图的技能树,通常地学习某个技能需要学习全部的前置技能,并且需要一定的花费,
但你可以通过氪金来消掉某个前置关系或者直接强行习得某个技能,
问要学习某个特定的技能需要的最小花费
[分析]
自己对最小割模型的理解还是太差了
建图方式——
每个点i拆成两个点i与i + n,i表示这个技能未学习,i + n表示这个技能已学习
1,如果我们可以通过z的成本,消除x之于y的前置关系,则我们建立从x + n向y建立容量为z的边
2,如果我们可以通过z的成本,在满足先决条件下正常习得技能x,则我们建立从ST 向 x建立容量为z的边
3,如果我们可以通过z的成本,氪金直接习得技能x,则我们建立从x 向 x + n建立容量为z的边
4,ed + n向ED建立容量为inf的边
然后跑最大流就是答案
理解:
最大流 = 最小割,这种建图方式其实是利用了网络流的割模型
然后跑出的最大流,就是指,最小的代价使得ST与ED不连通
因为n + ed 向 ED 的容量为inf,所以显然我们割掉的肯定不是这条边
我们可以选择——
1,割掉ed -> ed + n,这显然就是其氪金成本,其他一切条件都无所谓了,就把其前后的链接割掉了
2,割掉preed -> ed,我们要把ed与ed之前的点割掉
显然我们一定要割掉ST -> ed的边,也就是说,我们会计入修习这个技能的成本
同时我们可以学习ed之前的点x,也就是说,我们不割掉x + n -> ed,也就是说,我们需要习得x,
至于其怎么学,无所谓,但是必然要把其与ST的关系断开。
当然我们也可以不学习ed之前的点x,也就是说,我们割掉x + n -> ed,也就是说,我们消除了前置关系
这样子其实满好理解的,最小割就是最大流就是最小学习成本HDU1565 方格取数
[题意]
有一个n*n的棋盘,每个格子有一个非负数
我们希望取得若干个格子,使得任意两个格子不相邻
希望取得数的和最大
[分析]
超级源点向奇数点连边,流量为点权
奇数点向周围的点连边,流量为无限大
偶数点向超级汇点连边,流量为点权
然后总点权-最大流就是答案。
理解:
这道题求的其实是最大点权独立集
本题是个二分图,而二分图中,最大点权独立集 + 最小点权覆盖集 = 点权之和。
二分图中,覆盖集的点取走之后,就取走了这个图里的所有边,也就是取得了一个割。
我们求的是最小点权覆盖集,也就是求最小割,也就是跑最大流。中国大学生程序设计竞赛中南邀请赛(重现)F TC or CF
[题意]
n(50)场比赛
我们要给每场比赛定个性质,是TC还是CF
但是有限制条件——
1,第一场必须为TC
2,最后一场必须为CF
3,至少有3场TC
4,至少有3场CF
对于每个人而言,都参加两场比赛(自己会指明所选的第一场、第二场比赛各是哪一场)
对于第i个人而言,如果他选择的第一场是TC,第二场是CF,那么他就会有c[i]的unhappy值产生
问你如何安排,可以使得总的unhappy值最小,并输出
[分析]
这道题我们是如何做呢?网络流。
我们不确定哪些场次是TC,哪些场次是CF对不对?
我们可以枚举!
这个枚举方式。有2^n爆搜法(其实只要敢写就能过TwT)
有暴力枚举2场TC和2场CF的n^4法,然后跑网络流,不过会TLE
所以我们想要采取的处理方法是——
因为每个点不是TC就是CF,
于是我们暴力枚举第2、3、4这3个点(其实枚举2个点就可以,不过没有枚举3个点快)
这个枚举是2 ^ 3,然后已经确定了一些TC和CF,接着我们再枚举不够的点即可。
这样总的枚举复杂度是8 * 2500
之后我们再套上网络流——
对于网络流,我们建图方式是这样的:
超级源点向3个TC,连接容量无穷大的边
3个CF向超级汇点,连接容量无穷大的边
然后每个人所对应的第一个点与第二个点之间,连接容量为其unhappy值的边
这个时候我们再跑最大流,所有枚举方式之后的min{最大流}就是答案
为什么呢?这个模型本质其实是最小割,最小的成本使得不存在实效性质的TC - CF边,也就是答案啦[题意]
https://icpcarchive.ecs.baylor.edu/external/68/6887.pdf
有n(10000)个人,m(20000)个关系。
每个关系(x,y)表示第x个人想要第y个人手里的书——保证x != y即自己不喜欢自己的书,使得没有自环。
一些人会交换书的条件当且仅当这这些人拥有的书同喜欢的书形成了首尾相连的环
我们想要每个人都拿到一本自己喜欢的书,问能否实现。[分析]
每个点必须在一个环里,
就是每个点必须入度为1出度为1且不能有自环。
所以超级源点向每个入点建容量为1的边,
每个出点向超级汇点建容量为1的边,
这样每个点就有一个入一个出,他必然就要在一个环内才会使得最大流为nIntel Code Challenge Final Round (Div 1 + Div 2, Combined) E n个点有产品生产能力和销售能力C(n,2)条运输路径下有最大总销售量
[题意]
每个点有货物量p[i]和销售上限s[i]
对于任意的(i,j)(j>=i),第i个人可以向第j个人运输最多m件货物
问你,最好情况下我们可以卖出多少货物。
[分析]
我们知道,这道题有一个很显然的最大流解法,就是——
(源点->i,p[i]),(i->汇点,s[i]),(i->j,m),j>i
不过这个复杂度是吃不消的,光边数就是1e8级别开都开不下。
然而我们应该思考到,有一个常用的转化就是—— 最大流 = 最小割,
如果我们能求出这个图使得ST与ED不连通的最小割,问题也就能解啦。
不过如何最小割呢?我们思考一个DP。根据数据规模,我们考虑n^2的DP。
对于每个点而言,这个点要不最终属于集合ST,要不最终属于集合ED。
如果这个点i属于ST,则其需要割掉与ED边的边权(即割掉s[i])
如果这个点i属于ED,则其需要个点与ST边的边权,同时割掉与ST集合中其他点的连边(即割掉p[i] + numST * m)
也就是说,我们除了枚举当前点i,还需要知道之前在集合ST中选取了多少个点。
于是状态转移也就出来啦!
dp[i][j]表示我们选取从1开始考虑到了第i个点,在[1~i]中有j个点保持了与ST的连边不被割掉的当前最小割。
那么我们有状态转移——
考虑这个点在ED集合:dp[i][j] = dp[i - 1][j] + p[i] + j * m;
考虑这个点在ST集合:dp[i][j] = dp[i - 1][j - 1] + s[i];
最后的答案是min(f[n][0~n]【最大权闭合子图】
所谓最大权闭合子图,大概有这样的问题结构——
1,告诉你每个点的权值(权值有正有负)
2,点与点之间的选取,存在一定的依赖关系。
就比如说如果选了x,则必须要选择另外一些点{p1, p2, p3},即{px}是x的必要条件。
3,你需要决定选出若干点,进而使得点权之和最大
建图方式:
1,源点->正权点,容量为权值绝对值
2,负权点->汇点,容量为权值绝对值
3,对于如果选了x,就必须要选择y的情况,我们从x向y建立一条容量为inf的边
在这种情况下,我们跑最大流,得到最小割
与ST相连的点属于最大权闭合子图中的点,与ED相连的点不属于最大权闭合子图。
理解:
最大流 = 最小割,而最小割,意味着以最小成本,使得源点与汇点不连通。
在这种建图方式下——
割掉ST->正权点的边,意味着记下来所对应的成本太大了,我们不如放弃该正权
割掉负权点->ED的边,意味着我们虽然使用了这些成本,但是正权还没有被画完
而依赖关系之前的正无穷的边, 意味着这前驱节点和后继节点之间是不可同时割舍的
总的正权值 - 最小割, 就是问题的解HDU3996
[题目]
我们要挖矿,矿的层次为n(100),第i层有金矿数mi。
对于每个金矿,告诉你——
1.开采成本;2.开采收益;3.关联前驱金矿数g(就是必须把这g个开采了才能开采当前金矿)
然后接下来g行,是相关联金矿的层数和金矿编号。让你求最大收益。
[分析]
直接裸建图即可HDU3879
[题目]
有n(5000)个点和m(50000)条边
我们选择每个点,会有一个相应的花费p[]
如果一条边的两个点都选中了,那么我们便会得到一个相应的收益c[]
问你如何选择点,才能使得自己的纯利润最大。
[分析]
把边抽象为点
源点->边(边的收益)
边->端点(inf)
端点->汇点(端点的成本)
∑正权 - 就是答案HDU5855
[题目]
有n(200)个工厂和m(200)个商店
对于第i个工厂,工厂修建有成本c[i]和时间t[i]
对于第i个商店,如果其所需要的工厂都修建好了,其可以获利v[i]
我们希望在最少的时间内获利至少L(已知参数,int范围)
要你输出这个最小需要的时间t,然后在输出在这个时间下的最大获利值p
[分析]
1,显然我们可以二分时间,二分完时间之后,我们就知道哪些工厂可以考虑修建了。
2,接下来要建图,以判断是否能再这个时间内获得至少为L的利润
建图方式——
源点->商店(商店收益)
商店->工厂(无穷大)
工厂->汇点(建设成本)
跑最大权闭合子图,sum-正权边就是答案。HDU5772 2016 Multi-University Training Contest 4I
[题目]
给你一个长度为n(100)的数串,字符范围为'0'~'9'。
对于数字x,如果字符串中的有0个x,费用为0。
如果字符串中的有k(k>0)个x,费用为bx+(kx-1)*ax。
也就是说——第一个的费用为bx,接下来的每个费用都为ax。
同时我们有一个n*n的表w[i][j],
表示如果我们选择了位置i和位置j的字符,就会获得收益w[i][j]。
[分析]
首先,这个费用函数有些奇怪。
如果费用函数没这么奇怪的话,就是对于x,如果费用是kx * ax的话——
1,分析出来有一种特殊状态:(i与j一起选,获得收益w[i][j]+w[j][i])
2,如果我们有这种特殊状态,则我们意味着选i且选j,于是我们还要设置原始的每个点的选取情况。
即有(i&&j)->i(inf),(i&&j)->j(inf)
选位置意味着选数,即i -> num[i](inf),i -> num[j](inf)
注意:两种边不能可以合并,直接使得(i&&j) -> num[i](inf),(i&&j) -> num[j](inf)因为选了num[i]和num[j],并不意味着我们选择了位置i和位置j,所以还是要拆开考虑。
同时,选一个数会产生一定的费用,即这里我们有i->ED(ax)
这种建图模型可以解决kx * ax的问题,但是对于这道题中第一次选取成本为bx的情况就无法处理了。
于是我们考虑打补丁。
如果bx > ax,则意味着对于数字x,只要选了,在第一次选取的时候,会产生bx - ax的额外费用
于是我们只需要对于数字x,初始额外建一条x -> ED(bx - ax)的边即可
如果bx < ax,则意味着对于数字x,只要选了,在第一次选取的时候,会减少ax - bx的费用
也就是说,会获得ax - bx的收益,于是我们额外建一条ST -> x(ax - bx)的边即可。
接下来∑正权 - dinic() 就是答案【混合图欧拉回路】
HDU1956
[题意]
n(200)个点,m(1000)条边,
对于每条边,有属性z,如果z == 1,则该边为单向边,否则该边为双向边。
让你判断,在给每条双向边任意定向之后,能否在整个图中产生欧拉回路。
[分析]
网络流可以解决混合图求欧拉回路的问题
首先我们把双向边任意定向,并对于每个点,统计其在当前定向条件下的入度和出度。
如果对于任意点,(入度 - 出度) % 2 == 1,则无解。
否则我们设(ind[i] - oud[i]) / 2 = w
对于入度>出度的点(即w > 0),我们从这个点向ED连容量为abs(w)的边使之平衡化
对于出度>入度的点(即w < 0),我们从ST向这个点连容量为abs(w)的边使之平衡化
然后跑最大流。如果最大流为∑{w, w > 0},则表示问题有解。
理解:
对于入度 > 出度的点,其向ED连了容量为abs(w)的边都要流满
如果流满了,流出的容量为出度+w = 入度-w,也就是说,我们改变1/2的入度的方向即可
对于入度 < 出度的点,其从ST连了容量为abs(w)的边都要流满
如果流满了,流入的容量为入度+w = 出度-w,也就是说,我们改变1/2的出度的方向即可
于是这种建图能对应得到问题的解。
如果要输出方案,我们把所有从x到y没有流量的边反向即可
该边流满,说明我们选择了这条边;该边未满,说明这条边需要反向Codeforces Round 375 (Div 2) E One-Way Reform 双向图每条边定向使得最多的点满足入度=出度
[题意]
http://codeforces.com/contest/723/problem/E
n(200)个点m(C(n,2))条边的无向连通图,没有自环没有重边
我们要把所有点都定向,希望使得尽可能多的点拥有相同的入度与出度。
让你输出满足这个条件的最大点数和每条边最后的定向。
[分析]
首先,有一个猜想——
就是满足那个条件的最大点数为拥有偶数度数的点数。
首先,度数为奇数的点,显然不满足条件。
其次,度数为偶数的点,可以以形成欧拉回路的方式使得条件满足。
基于这个猜想,我们可以把度数为奇数的点配对连边
(注意:一定要配对而不能直接从ST连一条边了事,因为ST是特殊点)
这时的图一定存在一个欧拉回路,也就使得每个点的入度=出度,使得我们的猜想成立
我们可以用欧拉回路算法解决这道题,复杂度O(n+m)
也可以套用网络流解决,复杂度O(n*m)
网络流的建图方式是——
一开始把奇点之间配对连边,再使得所有点任意定向,
对于任意一个点,设w=abs(ind[i]-oud[i])/2
如果ind[i]>oud[i],就从i向ED连容量为w的边
如果oud[i]>ind[i],就从ST向i连容量为w的边
这时跑下最大流,如果满流,就存在欧拉回路HDU5639 Deletion 每次最多删环套树最少删除次数删光所有边
[题意]
http://acm.hdu.edu.cn/showproblem.php?pid=5639
有一个无向图G
图上有n(2000)个点和m(2000)条边
你可以选择删除一些边,对于删除的每个连通块,该连通块内,最多只能含有一个环。
问你最小的删除次数使得删光所有边。
[分析]
这道题我们思考最多只能含有一个环是什么东西——这个其实很熟悉,是环套树。
对于环套树,其可以是有向或无向的。对于有向环套树,其实每个点都恰好一条出度就好了。
假如说我们给原始图的每条边定了向。那么,该图中一个最大的出度数w,就必然对应着有至少w个环,至少消w次
于是,我们可以把问题转化——把无向图的边定向,使得最大点的出度尽可能小。
这个问题显然可以二分答案,而二分答案为V之后,我们要如何验证合法性呢?
把边定向,有点类似于混合图欧拉回路的求法。
我们设该点的出度为O
对于出度大于V的点,从源点向其连容量为O-V的边
对于出度小于V的点,由其向汇点连容量为V-O的边
然后我们跑最大流,如果最大流是满流的,则说明可行
理解:
如果满流,则说明对于出度大于V的点,我们把这些边反过来之后,可以在一些出度小于V的点上消耗掉。
于是满流就相当于满足的了最大出度<=V的要求*/
这篇关于网络流模板 网络流题型大荟萃的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!