【网络流24题】题解汇总

0. 前言

网络流算是在OI中一个博大精深的问题了。使用它解题的关键就是知道如何建图。网络流24题就是其中建图比较典型的题目了。下面我将按照我刷题的顺序写下每道题的解题报告。

注:部分题目在LOJ上题面和输入格式与洛谷上的有出路,此处以洛谷题面为准。

0.5 一些约定

( u , v , f ) (u,v,f) (u,v,f) 一条从 u u u v v v,流量为 f f f 的边。

( u , v , f , c ) (u,v,f,c) (u,v,f,c) 一条从 u u u v v v,流量为 f f f,费用为 c c c 的边。

1. 飞行员配对问题

题目链接:洛谷P2756LOJ 6000

题意:有 m + n m+n m+n 个飞行员,其中 m m m 个为英国皇家飞行员, n n n 个为外籍飞行员。已知有若干对皇家飞行员和外籍飞行员可以配对。求最多可以配成多少对,并输出方案。 1 ≤ n , m ≤ 100 1 \leq n,m \leq 100 1n,m100

题解:二分图最大匹配模板。但是由于是网络流24题,所以我这里写的是网络流的方法。

超级源点 S S S 1 1 1 m m m 连流量为 1 1 1 的边, m + 1 m+1 m+1 n n n 与超级汇点 T T T 连流量为 1 1 1 的边,中间连题目中所给的边,流量为 + ∞ +\infty +

至于输出方案,只要检查对于中读入的边,流过去的流量是否非零,即反向边的容量非零即可。

代码:

/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define put(x)		putchar(x)
#define eoln        put('\n')
#define space		put(' ')
inline int read(){
   
	int x=0,neg=1;char c=getchar();
	while(!isdigit(c)){
   
		if(c=='-')	neg=-1;
		c=getchar();
	}
	while(isdigit(c))	x=x*10+c-'0',c=getchar();
	return x*neg;
}
inline void print(int x){
   
	if(x<0){
   
		putchar('-');
		print(abs(x));
		return;
	}
	if(x<=9)	putchar(x+'0');
	else{
   
		print(x/10);
		putchar(x%10+'0');
	}
}
int n,m;
int ecnt=1,head[100005],dep[100005];
struct edge{
   
	int to,next,cap;
} e[100005];
bool b[100005];
inline void addedge(int u,int v,int f){
   
	e[++ecnt].to=v;e[ecnt].cap=f;e[ecnt].next=head[u];head[u]=ecnt;
}
inline bool bfs(int s,int t){
   
	queue<int> q;
	memset(dep,-1,sizeof(dep));
	q.push(s);dep[s]=0; 
	while(!q.empty()){
   
		int now=q.front();q.pop();
		for(int i=head[now];i;i=e[i].next){
   
			int v=e[i].to;
			if(dep[v]==-1&&e[i].cap){
   
				dep[v]=dep[now]+1;
				q.push(v);
			}
		}
	}
	if(dep[t]!=-1)	return 1;
	return 0;
}
inline int dfs(int x,int t,int f){
   
	if(x==t)	return f;
	int w,ret=0;
	for(int i=head[x];i;i=e[i].next){
   
		int v=e[i].to;
		if(dep[v]==dep[x]+1&&e[i].cap){
   
			w=dfs(v,t,min(f-ret,e[i].cap));
			e[i].cap-=w;
			e[i^1].cap+=w;
			ret+=w;
			if(ret==f)	return f;
		}
	}
	if(!ret)	dep[x]=-1;
	return ret;
}
int dinic(int s,int t){
   
	int tot=0;
	while(bfs(s,t))
		tot+=dfs(s,t,0x3f3f3f3f);
	return tot;
}
int main(){
   //源点0,汇点n+1
	scanf("%d%d",&m,&n);
	while(1){
   
		int x,y;scanf("%d%d",&x,&y);
		if(x==-1&&y==-1)	break;
		addedge(x,y,0x3f3f3f3f);//建中间的边
		addedge(y,x,0);
	}
	for(int i=1;i<=m;i++)	addedge(0,i,1),addedge(i,0,0);//建源点连向1~m的边
	for(int i=m+1;i<=n;i++)	addedge(i,n+1,1),addedge(n+1,i,0);//建m+1~n连向汇点的边
	int tot=dinic(0,n+1);
	if(tot==0){
   
		puts("No Solution!");
		return 0;
	}
	printf("%d\n",tot);
	for(int i=2;i<=ecnt;i=i+2){
   
		if(e[i].to!=n+1&&e[i^1].to!=0)
			if(e[i].to!=n+1&&e[i^1].to!=n+1)//判断是否为中间的边
				if(e[i^1].cap!=0){
   //如果反向边容量不为0
					printf("%d %d\n",e[i^1].to,e[i].to);
				}
	}
	return 0;
}

2. 软件补丁问题

题目链接:洛谷P2761LOJ 6009

题意:有一个公司要应对软件中的 n n n b u g bug bug ,有 m m m 个修复 b u g bug bug 的程序,对于每个程序有四个集合 b i , 1 , b i , 2 , f i , 1 , f i , 2 b_{i,1},b_{i,2},f_{i,1},f_{i,2} bi,1,bi,2,fi,1,fi,2,如果当前 b u g bug bug 的集合包含 b i , 1 b_{i,1} bi,1 中的所有 b u g bug bug 并且不包含 b i , 2 b_{i,2} bi,2 中的所有 b u g bug bug 的时候,使用这个程序可以花费 t i t_i ti 的时间修复 f i , 1 f_{i,1} fi,1 中的所有 b u g bug bug,而又会新增添 f 2 , i f_{2,i} f2,i 中的所有 b u g bug bug。求修复所有 b u g bug bug 需要的最小时间。 1 ≤ n ≤ 20 1 \leq n \leq 20 1n20 1 ≤ m ≤ 100 1 \leq m \leq 100 1m100

题解:这道题不是网络流啊!

状压 d p dp dp d p i dp_i dpi 表示当 b u g bug bug 集合为 i i i 的时候所需要的时间,最短路转移,就可以过了。

/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define put(x)		putchar(x)
#define eoln        put('\n')
#define space		put(' ')
inline int read(){
   
	int x=0,neg=1;char c=getchar();
	while(!isdigit(c)){
   
		if(c=='-')	neg=-1;
		c=getchar();
	}
	while(isdigit(c))	x=x*10+c-'0',c=getchar();
	return x*neg;
}
inline void print(int x){
   
	if(x<0){
   
		putchar('-');
		print(abs(x));
		return;
	}
	if(x<=9)	putchar(x+'0');
	else{
   
		print(x/10);
		putchar(x%10+'0');
	}
}
int n,m,a[105],dp[1<<21],b1[105],b2[105],f1[105],f2[105];
bool legal(int s,int i){
   //判断状态为s的时候是否可以使用程序i
	if((b1[i]|s)==s&&!(b2[i]&s))	return true;
	return false;
}
void spfa(){
   
	queue<int> q;
	q.push((1<<n)-1);
	dp[(1<<n)-1]=0;
	while(!q.empty()){
   
		int s=q.front();
		q.pop();
		for(int i=1;i<=m;i++)
			if(legal(s,i)){
   
				int t=(s&(~f1[i]))|f2[i];
				if(dp[s]+a[i]<dp[t]){
   
					dp[t]=dp[s]+a[i];//最短路转移
					q.push(t);
				}
			}
	}
}
int main(){
   
	memset(dp,1,sizeof(dp));
	cin>>n>>m;
	for(int i=1;i<=m&&cin>>a[i];i++){
   
		char ch;
		for(int j=1;j<=n;j++){
   
			char ch;cin>>ch;
			if(ch=='+')
				b1[i]+=1<<j-1;
			else if(ch=='-')
				b2[i]+=1<<(j-1);
		}
		for(int j=1;j<=n;j++){
   
			char ch;cin>>ch;
			if(ch=='-')
				f1[i]+=1<<(j-1);
			else if(ch=='+')
				f2[i]+=1<<(j-1);
		}
	}
	spfa();
	cout<<(dp[0]==0x01010101?0:dp[0])<<endl;
	return 0;
}

3. 餐巾计划问题

题目链接:洛谷P1251LOJ 6008

题意:有一个餐厅在 n n n 天中第 i i i 天要用 r i r_i ri 块餐巾。餐厅可以购买新的餐巾,每块餐巾的费用为 p p p 分,或者把用过的餐巾送到快洗部,洗一块需 m m m 天,费用为 f f f 分,或者送到慢洗部,洗一块需 l l l 天,费用为 s s s 分。 1 ≤ n ≤ 2000 1 \leq n \leq 2000 1n2000

题解:费用流的经典题,建图较为恶心。

看了洛谷的题解

我们将每一天拆成早上和晚上两个点,每天晚上会受到用过的餐巾,每天早上又会受到干净的餐巾。

按以下方式建图:

  1. 从源点 S S S 向每一天晚上表示的节点 j j j 连一条边 ( S , j , r i , 0 ) (S,j,r_i,0) (S,j,ri,0),表示每天夜晚获得 r i r_i ri 条用过的餐巾。
  2. 从每一天早上表示的节点 j j j 向汇点 T T T 连一条边 ( j , T , r i , 0 ) (j,T,r_i,0) (j,T,ri,0) 表示向汇点提供 r i r_i ri 条干净的餐巾,流满时表示第 i i i 天的餐巾够用。
  3. 从每一天晚上 j j j 向第二天晚上 k k k 连一条边 ( j , k , + ∞ , 0 ) (j,k,+\infty,0) (j,k,+,0),表示每天晚上可以将用过的餐巾留到第二天晚上。
  4. 从每一天晚上 j j j 向这一天过了快洗所用天数 m m m 的那一天早上 k k k 连一条边 ( j , k , + ∞ , f ) (j,k,+\infty,f) (j,k,+,f),表示每天晚上可以将用过的餐巾送去快洗部,在第 i + m i+m i+m 天早上收到干净的餐巾。
  5. 同理,从每一天晚上 j j j 向这一天过了慢洗所用天数 l l l 的那一天早上 k k k 连一条边 ( j , k , + ∞ , s ) (j,k,+\infty,s) (j,k,+,s),意义同上。
  6. 从源点 S S S 向每一天早上 j j j 连一条边 ( S , j , + ∞ , p ) (S,j,+\infty,p) (S,j,+,p),表示每天早上可以购买干净的餐巾。

然后跑最小费用最大流。

这里用到了一个最小(大)费用最大流的一个重要思想,最大流保证构造满足题目条件(如本题中每一天早上向汇点连边,流满表示够用,如果不流满就不合法了),最小(大)费用保证代价最小。

/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define put(x)		putchar(x)
#define eoln        put('\n')
#define space		put(' ')
#define int long long
inline int read(){
   
	int x=0,neg=1;char c=getchar();
	while(!isdigit(c)){
   
		if(c=='-')	neg=-1;
		c=getchar();
	}
	while(isdigit(c))	x=x*10+c-'0',c=getchar();
	return x*neg;
}
inline void print(int x){
   
	if(x<0){
   
		putchar('-');
		print(abs(x));
		return;
	}
	if(x<=9)	putchar(x+'0');
	else{
   
		print(x/10);
		putchar(x%10+'0');
	}
}
int ecnt=1,head[100005];
struct edge{
   
	int to,nxt,cap,cost;
} e[100005];
inline void addedge(int u,int v,int f,int c){
   
	e[++ecnt].to=v;e[ecnt].cap=f;e[ecnt].cost=c;e[ecnt].nxt=head[u];head[u]=ecnt;
}
bool vis[100005];
int dist[100005],flow[100005],pre[100005],pos[100005];
inline bool spfa(int s,int t){
   
	memset(vis,1,sizeof(vis));
	memset(dist,63,sizeof(dist));
	queue<int> q;
	vis[s]=0;
	dist[s]=0;
	flow[s]=0x3f3f3f3f;
	q.push(s);
	while(!q.empty()){
   
		int x=q.front();
//		cout<<x<<endl;
		vis[x]=true;
		for(int i=head[x];i;i=e[i].nxt){
   
			int y=e[i].to;
//			cout<<y<<endl;
			if(e[i].cap>0&&dist[y]>dist[x]+e[i].cost){
   
				dist[y]=dist[x]+e[i].cost;pos[y]=x;pre[y]=i;
				flow[y]=min(flow[x],e[i].cap);
//				cout<<flow[y]<<endl;
				if(vis[y]){
   
					q.push(y);
					vis[y]=false;
				}
			}
		}
		q.pop();
	}
//	cout<<flow[t]<<endl;
	return dist[t]<0x3f3f3f3f3f3f3f3fll;
}
inline int Dinic(int s,int t){
   
	int sum=0;
	while(spfa(s,t)){
   
		sum+=flow[t]*dist[t];
		for(int i=t;i!=s;i=pos[i]){
   
			e[pre[i]].cap-=flow[t];
			e[pre[i]^1].cap+=flow[t];
		}
	}
	return sum;
}
int n,m,t1,t2,m1,m2;
signed main(){
   //源点0,汇点2n+1
	scanf("%lld",&n);
	int st=0,ed=2*n+1;
	for(int i=1;i<=n;i++){
   
		int x;scanf("%lld",&x);
		addedge(st,i,x,0);addedge(i,st,0,0);//条件1
		addedge(i+n,ed,x,0);addedge(ed,i+n,0,0);//条件2
    }
    scanf("%lld%lld%lld%lld%lld",&m,&t1,&m1,&t2,&m2);
    for(int i=1;i<=n;i++){
   
		if(i+1<=n)	addedge(i,i+1,1e9,0),addedge(i+1,i,0,0); //条件3
		if(i+t1<=n)	addedge(i,i+n+t1,1e9,m1),addedge(i+n+t1,i,0,-m1);//条件4
		if(i+t2<=n)	addedge(i,i+n+t2,1e9,m2),addedge(i+n+t2,i,0,-m2);//条件5
		addedge(st,i+n,1e9,m);addedge(i+n,st,0,-m);//条件6
	}
	cout<<Dinic(st,ed)<<endl;
	return 0;
}

4. 深海机器人问题

题目链接:洛谷P4012LOJ 6224

题意:有一张网格图,左下角 ( 0 , 0 ) (0,0) (0,0),右上角 ( Q , P ) (Q,P) (Q,P),有 a a a 个出发位置,对于每一个位置给出三个数 k , x , y k,x,y k,x,y,表示有 k k k 个机器人从 ( x , y ) (x,y) (x,y) 出发。有 b b b 个终点,也给出三个数 k , x , y k,x,y k,x,y,表示有 k k k 个机器人要到 ( x , y ) (x,y) (x,y)。每个机器人可以向上或向右走到终点。每条向右或向上的路径上都有一个生物标本,采集一个生物标本可以获得一些价值。一条路上如果生物标本已被采集过就没有了。求总价值的最大值。

题解:费用流的气息很明显。

建出图来,从源点连向每一个起点连一条边 ( S , i , k , 0 ) (S,i,k,0) (S,i,k,0),再从每一个终点连向汇点连一条边 ( i , T , k , 0 ) (i,T,k,0) (i,T,k,0)。对于每一条网格图中的道路 ( x , y , z ) (x,y,z) (x,y,z),连两条边 ( x , y , 1 , z ) (x,y,1,z) (x,y,1,z) ( x , y , ∞ , 0 ) (x,y,\infty,0) (x,y,,0),因为道路可以通过多次,但标本只能收集一次。然后跑最大费用最大流。这里有一个技巧,将每条边的价值都取相反数,这样就变成了一个最小费用最大流,然后答案再取相反数就行了。

/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define put(x)		putchar(x)
#define eoln        put('\n')
#define space		put(' ')
#define int long long//开long long
inline int read(){
   
	int x=0,neg=1;char c=getchar();
	while(!isdigit(c)){
   
		if(c=='-')	neg=-1;
		c=getchar();
	}
	while(isdigit(c))	x=x*10+c-'0',c=getchar();
	return x*neg;
}
inline void print(int x){
   
	if(x<0){
   
		putchar('-');
		print(abs(x));
		return;
	}
	if(x<=9)	putchar(x+'0');
	else{
   
		print(x/10);
		putchar(x%10+'0');
	}
}
int ecnt=1,head[100005];
struct edge{
   
	int to,nxt,cap,cost;
} e[100005];
inline void addedge(int u,int v,int f,int c){
   
	e[++ecnt].to=v;e[ecnt].cap=f;e[ecnt].cost=c;e[ecnt].nxt=head[u];head[u]=ecnt;
	e[++ecnt].to=u;e[ecnt].cap=0;e[ecnt].cost=-c;e[ecnt].nxt=head[v];head[v]=ecnt;
}
bool vis[100005];
int dist[100005],flow[100005],pre[100005],pos[100005];
inline bool spfa(int s,int t){
   
	memset(vis,1,sizeof(vis));
	memset(dist,63,sizeof(dist));
	queue<int> q;
	vis[s]=0;
	dist[s]=0;
	flow[s]=0x3f3f3f3f;
	q.push(s);
	while(!q.empty()){
   
		int x=q.front();
//		cout<<x<<endl;
		vis[x]=true;
		for(int i=head[x];i;i=e[i].nxt){
   
			int y=e[i].to;
//			cout<<y<<endl;
			if(e[i].cap>0&&dist[y]>dist[x]+e[i].cost){
   
				dist[y]=dist[x]+e[i].cost;pos[y]=x;pre[y]=i;
				flow[y]=min(flow[x],e[i].cap);
//				cout<<flow[y]<<endl;
				if(vis[y]){
   
					q.push(y);
					vis[y]=false;
				}
			}
		}
		q.pop();
	}
//	cout<<flow[t]<<endl;
	return dist[t]<0x3f3f3f3f3f3f3f3fll;
}
inline int Dinic(int s,int t){
   
	int sum=0;
	while(spfa(s,t)){
   
		sum+=flow[t]*dist[t];
		for(int i=t;i!=s;i=pos[i]){
   
			e[pre[i]].cap-=flow[t];
			e[pre[i]^1].cap+=flow[t];
		}
	}
	return sum;
}
int a=read(),b=read(),n=read(),m=read();
inline int id(int x,int y){
   
	return (m+1)*x+y+1;
}
signed main(){
   //源点1234,汇点5678(不要问我为什么)
	fz(i,0,n){
   
		fz(j,0,m-1){
   
			int num=read();
			addedge(id(i,j),id(i,j+1),1,-num);
			addedge(id(i,j),id(i,j+1),0x3f3f3f3f,0);//向每个点东边节点连边
		}
	}
	fz(i,0,m){
   
		fz(j,0,n-1){
   
			int num=read();
			addedge(id(j,i),id(j+1,i),1,-num);
			addedge(id(j,i),id(j+1,i),0x3f3f3f3f,0);//向每个点南边节点连边
		}
	}
	fz(i,1,a){
   
		int k=read(),x=read(),y=read();
		addedge(1234,id(x,y),k,0);//从源点向每个起点连边
	}
	fz(i,1,b){
   
		int k=read(),x=read(),y=read();
		addedge(id(x,y),5678,k,0);//从每个终点向汇点连边
	}
	cout<<-Dinic(1234,5678)<<endl;
	return 0;
}

5. 方格取数问题

题目链接:洛谷P2774LOJ 6007

题意:有一个 n × m n \times m n×m 的矩阵,你可以从中选取一些元素使得任意两个数都不相邻,求和的最大值。

解法:网络流之最小割

我们考虑先选中所有方格,再想办法删去权值和尽量小的一批方格。

我们可以建一张图,节点表示矩阵中每一个元素,两节点之间有一条边表示这两个点相邻。我们不难发现,当把矩阵进行黑白间隔染色,相邻两点一定是一黑一白,因此我们构造出的矩阵一定是一个二分图。

那么思路就出来了。我们建一个虚拟源点 S S S,和虚拟汇点 T T T,我们对于所有白色格子上的点,连一条从 S S S 到这个点的边,流量为点权,删掉这条边,就表明不选这个点。同理,对于所有黑色格子上的点,连一条从这个点到 T T T 的边,流量为点权,删掉这条边也表明不选这个点。而二分图内部连着互斥的点,边权为 i n f inf inf (即删掉这条边没有意义),那么我们要求的就是这张图的最小割,根据最大流 = = = 最小割可以通过跑一次最大流求出答案。

/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define put(x)		putchar(x)
#define eoln        put('\n')
#define space		put(' ')
inline int read(){
   
	int x=0,neg=1;char c=getchar();
	while(!isdigit(c)){
   
		if(c=='-')	neg=-1;
		c=getchar();
	}
	while(isdigit(c))	x=x*10+c-'0',c=getchar();
	return x*neg;
}
inline void print(int x){
   
	if(x<0){
   
		putchar('-');
		print(abs(x));
		return;
	}
	if(x<=9)	putchar(x+'0');
	else{
   
		print(x/10);
		putchar(x%10+'0');
	}
}
int n=read(),m=read();
int dx[]={
   1,0,-1,0};
int dy[]={
   0,1,0,-1};
inline int id(int x,int y){
   
	return (x-1)*m+y;
}
int head[100005];
struct edge{
   
	int to,nxt,cap;
} e[100005];
int ecnt=1;
inline void addedge(int u,int v,int f){
   
	e[++ecnt].to=v;e[ecnt].cap=f;e[ecnt].nxt=head[u];head[u]=ecnt;
}
int dep[100005];
inline bool bfs(int s,int t){
   
	queue<int> q;
	memset(dep,-1,sizeof(dep));
	q.push(s);dep[s]=0;
	while(!q.empty()){
   
		int cur=q.front();q.pop();
		for(int i=head[cur];i;i=e[i].nxt){
   
			int to=e[i].to;
			if(dep[to]==-1&&e[i].cap){
   
				dep[to]=dep[cur]+1;
				q.push(to);
			}
		}
	}
	if(dep[t]!=-1)	return 1;
	return 0;
}
inline int dfs(int x,int t,int f){
   
	if(x==t)	return f;
	int ret=0;
	for(int i=head[x];i;i=e[i].nxt){
   
		int y=e[i].to;
		if(dep[y]==dep[x]+1&&e[i].cap){
   
			int w=dfs(y,t,min(f-ret,e[i].cap));
			e[i].cap-=w;
			e[i^1].cap+=w;
			ret+=w;
			if(ret==f)	return f;
		}
	}
	return ret;
}
int sum=0;
inline int Dinic(int s,int t){
   
	int tot=0;
	while(bfs(s,t))	tot+=dfs(s,t,0x3f3f3f3f);
	return tot;
}
int main(){
   //源点0,汇点nm+1
	fz(i,1,n)	fz(j,1,m){
   
		int val=read();
		sum+=val;
		if((i+j)%2){
   
			for(int k=0;k<4;k++){
   
				
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值