[网络流]: 网络流24题

1.分配问题

题目描述 Description

有n件工作要分配给n个人做。第i 个人做第j 件工作产生的效益为ij c 。试设计一个将
n件工作分配给n个人做的分配方案,使产生的总效益最大。
«编程任务:
对于给定的n件工作和n个人,计算最优分配方案和最差分配方案。

输入描述 Input Description

第1 行有1 个正整数n,表示有n件工作要分配给n 个人做。接下来的n 行中,每行有n 个整数 cij ,1≤i≤n,1≤j≤n,表示第i 个人做第j件工作产生的效益为cij

输出描述 Output Description

将计算出的最小总效益和最大总效益输出

样例输入 Sample Input

5
2 2 2 1 2
2 3 1 2 4
2 0 1 1 1
2 3 4 3 3
3 2 1 2 1

样例输出 Sample Output

5
14

题解

此题比较简单直接拆点 一连 跑最小费用就好 

求最大时可以写个build函数(借鉴hwer写法) 将边权值变为负数 在重跑一边最小费用 输出-min_cost

#include
#include
#include
#include
#include
#include
#include
#define inf 0x7ffffff
using namespace std;
int cnt=1,d[2005],head[2005],flag[2005],from[2005],start=0,end=2001,max_cost=0,mp[1005][1005],n;
struct edge{int to,next,flow,cost;}e[1000001];
void ini(int x,int y,int flow,int cost){e[++cnt].to=y;e[cnt].flow=flow;e[cnt].cost=cost;e[cnt].next=head[x];head[x]=cnt;}
void insert(int x,int y,int flow,int cost){ini(x,y,flow,cost);ini(y,x,0,-cost);}
queueq;
bool spfa(){
	memset(d,127,sizeof(d));
	d[start]=0;q.push(start);flag[start]=1;
	while(!q.empty()){
		int k=q.front();q.pop();flag[k]=0;
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(e[i].flow>0&&d[kk]>d[k]+e[i].cost)
			{
				d[kk]=d[k]+e[i].cost;from[kk]=i;
				if(!flag[kk]){
					flag[kk]=1;
					q.push(kk);
				}
			}
		}
	}
	return d[end]

2.负载平衡问题

题目描述 Description

G 公司有n 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最
少搬运量可以使n 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
«编程任务:
对于给定的n 个环形排列的仓库的库存量,编程计算使n 个仓库的库存数量相同的最少
搬运量。

输入描述 Input Description

第1 行中有1 个正整数n(n<=100),表示有n
个仓库。第2 行中有n个正整数,表示n个仓库的库存量。

输出描述 Output Description

将计算出的最少搬运量输出

样例输入 Sample Input

5
17 9 14 16 4

样例输出 Sample Output

11

题解

每个点拆成xi,yi 先算平均数 源点连比平均数大的xi 汇点连比平均数小的yi(因为从xi运到yi) 把多货的点和缺货的分为两个点去连图

跑的时候从左到右跑 所以多货的往少货的运以下是网上的解释
首先求出所有仓库存货量平均值,设第i个仓库的盈余量为A[i],A[i] = 第i个仓库原有存货量 - 平均存货量。建立二分图,把每个仓库抽象为两个节点Xi和Yi。增设附加源S汇T。
1、如果A[i]>0,从S向Xi连一条容量为A[i],费用为0的有向边。
2、如果A[i]<0,从Yi向T连一条容量为-A[i],费用为0的有向边。
3、每个Xi向两个相邻顶点j,从Xi到Xj连接一条容量为无穷大,费用为1的有向边,从Xi到Yj连接一条容量为无穷大,费用为1的有向边。
求最小费用最大流,最小费用流值就是最少搬运量。

#include
#include
#include
#include
#include
#include
#include
#define inf 0x7ffffff
using namespace std;
struct edge{int to,next,flow,cost;}e[1000001];
int d[2005],head[2005],flag[2005],from[2005],cnt=1;int start=0,end=2001,min_cost=0,n,a[2005];
void ini(int x,int y,int flow,int cost){e[++cnt].to=y;e[cnt].flow=flow;e[cnt].cost=cost;e[cnt].next=head[x];head[x]=cnt;}
void insert(int x,int y,int flow,int cost){ini(x,y,flow,cost);ini(y,x,0,-cost);}
queueq;
bool spfa(){
	memset(d,127,sizeof(d));
	d[start]=0;q.push(start);flag[start]=1;
	while(!q.empty()){
		int k=q.front();q.pop();flag[k]=0;
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(e[i].flow>0&&d[kk]>d[k]+e[i].cost)
			{
				d[kk]=d[k]+e[i].cost;from[kk]=i;
				if(!flag[kk]){
					flag[kk]=1;
					q.push(kk);
				}
			}
		}
	}
	return d[end]0)	insert(start,i,a[i],0);
		else if(a[i]<0) insert(i+n,end,-a[i],0);
		int j=(i==1?n:i-1);
		insert(i,j,inf,1);insert(i,j+n,inf,1);
		j=(i==n?1:i+1);
		insert(i,j,inf,1);insert(i,j+n,inf,1);
	}
	mcf();
	printf("%d",min_cost);
	return 0;
}

3.飞行员配对问题


题目描述 Description

第二次世界大战时期,英国皇家空军从沦陷国征募了大量外籍飞行员。由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另1 名是外籍飞行员。在众多的飞行员中,每一名外籍飞行员都可以与其他若干名英国飞行员很好地配合。如何选择配对飞行的飞行员才能使一次派出最多的飞机。对于给定的外籍飞行员与英国飞行员的配合情况,试设计一个算法找出最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。

对于给定的外籍飞行员与英国飞行员的配合情况,编程找出一个最佳飞行员配对方案,使皇家空军一次能派出最多的飞机。

输入描述 Input Description

由文件input.txt提供输入数据。文件第1 行有2个正整数m和n。n是皇家空军的飞行员总数(n<100);m是外籍飞行员数。外籍飞行员编号为1~m;英国飞行员编号为m+1~n。接下来每行有2 个正整数i和j,表示外籍飞行员i可以和英国飞行员j配合。文件最后以2个-1 结束。

输出描述 Output Description

程序运行结束时,将最佳飞行员配对方案输出到文件output.txt 中。第1 行是最佳飞行员配对方案一次能派出的最多的飞机数M。接下来M 行是最佳飞行员配对方案。每行有2个正整数i和j,表示在最佳飞行员配对方案中,飞行员i和飞行员j 配对。如果所求的最佳飞行员配对方案不存在,则输出‘No Solution!’。

样例输入 Sample Input

10 5
1 7
2 6
2 10
3 7
4 8
5 9

样例输出 Sample Output

4

题解

就是一个裸的二分图匹配 可以用网络流和二分图匹配两种方法做 显然二分图匹配更好打

二分图:

#include
#include
#include
#include
#include
#include
using namespace std;
int n,m;
int flag[2005],mp[2005][2005],mat[2005];
bool km(int x){
	for(int i=m;i<=n;i++)
	{
		if(mp[x][i]&&!flag[i]){
			flag[i]=1;
			if(!mat[i]||km(mat[i])){
				mat[i]=x;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	scanf("%d%d",&m,&n);
	int x,y;
	while(scanf("%d%d",&x,&y)){
		if(x==-1&&y==-1) break;
		mp[x][y]=mp[y][x]=1;
	}
	int ans=0;
	for(int i=1;i<=m;i++){
		memset(flag,0,sizeof(flag));
		if(km(i)) ans++;
	}
	if(ans==0)
	printf("No Solution!\n");
	else
	{
		printf("%d\n",ans);
		for(int i=m+1;i<=n;i++)
		{
			if(mat[i])
			printf("%d %d\n",mat[i],i);
		}
	}
	return 0;
}
网络流:

#include
#include
#include
#include
#include
#include
#include
#define inf 0x7ffffff
using namespace std;
int d[2005],head[2005],ans=0,cnt=1,n,m;
int start=0,end=2001;
struct edge{int to,next,flow;}e[100001];
queueq;
void ini(int x,int y,int flow){
	e[++cnt].to=y;e[cnt].flow=flow;e[cnt].next=head[x];head[x]=cnt;
}
void insert(int x,int y,int flow){
	ini(x,y,flow);ini(y,x,0);
}
bool bfs(){
	memset(d,-1,sizeof(d));
	d[start]=0;
	q.push(start);
	while(!q.empty()){
		int k=q.front();q.pop();
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(d[kk]==-1&&e[i].flow>0){
				d[kk]=d[k]+1;
				q.push(kk);
			}
		}
	}
	return d[end]!=-1;
}
int dfs(int x,int f){
	if(x==end) return f;
	for(int i=head[x];i;i=e[i].next){
		int k=e[i].to;int a;
		if(d[k]==d[x]+1&&e[i].flow>0&&(a=dfs(k,min(e[i].flow,f))))
		{
			e[i].flow-=a;
			e[i^1].flow+=a;
			return a;
		}
	}
	return 0;
}
void dinic(){
	while(bfs()){
		int a;
		while(a=dfs(start,inf))
			ans+=a;
	}
}
int main(){
	scanf("%d%d",&m,&n);
	int x,y;
	for(int i=1;i<=m;i++) insert(start,i,1);
	for(int i=m+1;i<=n;i++) insert(i,end,1);
	while(scanf("%d%d",&x,&y)){
		if(x==-1&&y==-1) break;
		insert(x,y,1);
	}
	dinic();
	if(ans==0)
	printf("No Solution!\n");
	else
		printf("%d\n",ans);
	return 0;
}

4.餐巾计划问题

题目描述 Description

一个餐厅在相继的 N 天里,每天需用的餐巾数不尽相同。假设第 i 天需要 ri块餐巾(i=1,2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 p 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 n 天(n>m),其费用为 s 每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 N 天中餐巾使用计划,使总的花费最小。
编程找出一个最佳餐巾使用计划.

输入描述 Input Description

第 1 行有 6 个正整数 N,p,m,f,n,s。N 是要安排餐巾使用计划的天数;p 是每块新餐巾的费用;m 是快洗部洗一块餐巾需用天数;f 是快洗部洗一块餐巾需要的费用;n 是慢洗部洗一块餐巾需用天数;s 是慢洗部洗一块餐巾需要的费用。接下来的 N 行是餐厅在相继的 N 天里,每天需用的餐巾数。

输出描述 Output Description

将餐厅在相继的 N 天里使用餐巾的最小总花费输出

样例输入 Sample Input

3 10 2 3 3 2

5

6

7

样例输出 Sample Output

145

题解

二分图的思想
把每个点分为:

今天用了要去洗的(i-day) 和 洗好了可以去用的(day+1-2*day)  然后就是连边 分四种情况(还可以留到明天再洗 费用为0) 

注意细节就好了 跑最小费用最大流

#include
#include
#include
#include
#include
#include
#include
#define inf 0x7ffffff
using namespace std;
int day,p,m,f,n,s;int start=0,end=2001,cnt=1;
int d[2005],flag[2005],head[2005],from[2005],min_cost=0;
struct edge{int to,next,flow,cost;}e[1000001];
queueq;
void ini(int x,int y,int flow,int cost){e[++cnt].to=y;e[cnt].flow=flow;e[cnt].cost=cost;e[cnt].next=head[x];head[x]=cnt;}
void insert(int x,int y,int flow,int cost){ini(x,y,flow,cost);ini(y,x,0,-cost);}
bool spfa(){
	memset(d,127,sizeof(d));
	q.push(start);
	d[start]=0;flag[start]=1;
	while(!q.empty()){
		int k=q.front();q.pop();flag[k]=0;
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(e[i].flow>0&&d[kk]>d[k]+e[i].cost)
			{
				d[kk]=d[k]+e[i].cost;from[kk]=i;
				if(!flag[kk]){
					flag[kk]=1;
					q.push(kk);
				}
			}
		}
	}
	return d[end]

5.运输问题

题目描述 Description

W 公司有m个仓库和n 个零售商店。第i 个仓库有ai 个单位的货物;第j 个零售商店
需要bj个单位的货物。货物供需平衡,即  sum(si)=sum(bj)
。从第i 个仓库运送每单位货物到
第j 个零售商店的费用为cij 。试设计一个将仓库中所有货物运送到零售商店的运输方案,
使总运输费用最少。
编程任务:
对于给定的m 个仓库和n 个零售商店间运送货物的费用,计算最优运输方案和最差运输方案。

输入描述 Input Description

的第1行有2 个正整数m和n,分别表示仓库数和
零售商店数。接下来的一行中有m个正整数ai ,1≤i≤m,表示第i个仓库有ai 个单位的货
物。再接下来的一行中有n个正整数bj ,1≤j≤n,表示第j个零售商店需要bj 个单位的货
物。接下来的m行,每行有n个整数,表示从第i 个仓库运送每单位货物到第j个零售商店
的费用cij 。

输出描述 Output Description

将计算出的最少运输费用和最多运输费用输出

样例输入 Sample Input

2 3
220 280
170 120 210
77 39 105
150 186 122

样例输出 Sample Output

48500
69140

题解

比较简单就不说了 就是费用流裸题

#include
#include
#include
#include
#include
#include
#include
#define inf 0x7ffffff
using namespace std;
int n,m;int start=0,end=2001,cnt=1;
int d[2005],c[2005][2005],a[2005],b[2005],flag[2005],head[2005],from[2005],min_cost;
struct edge{int to,next,flow,cost;}e[1000001];
queueq;
void ini(int x,int y,int flow,int cost){e[++cnt].to=y;e[cnt].flow=flow;e[cnt].cost=cost;e[cnt].next=head[x];head[x]=cnt;}
void insert(int x,int y,int flow,int cost){ini(x,y,flow,cost);ini(y,x,0,-cost);}
bool spfa(){
	memset(d,127,sizeof(d));
	q.push(start);
	d[start]=0;flag[start]=1;
	while(!q.empty()){
		int k=q.front();q.pop();flag[k]=0;
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(e[i].flow>0&&d[kk]>d[k]+e[i].cost)
			{
				d[kk]=d[k]+e[i].cost;from[kk]=i;
				if(!flag[kk]){
					flag[kk]=1;
					q.push(kk);
				}
			}
		}
	}
	return d[end]

6.太空飞行计划问题

Description

W教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合 I={I1,I2,…In}。实验 Ej需要用到的仪器是 I的子集。配置仪器 Ik的费用为 ck美元。实验 Ej的赞助商已同意为该实验结果支付 pj美元。W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。

对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。

Input

文件第 1行有 2个正整数 m和 n。m是实验数,n是仪器数。

接下来的 m行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。

最后一行的 n个数是配置每个仪器的费用。

Output

程序运行结束时,将最佳实验方案输出。第 1行是实验编号;第 2行是仪器编号;最后一行是净收益。

Sample Input

2 3
10 1 2
25 2 3
5 6 7

Sample Output

1 2
1 2 3
17

HINT

n,m < 50
题解

有点难。。但是建好图就简单了 转换为最小割 进而转为最大流

下面是解析:

【问题分析】
最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。
【建模方法】


把每个实验看作二分图X集合中的顶点,每个设备看作二分图Y集合中的顶点,增加源S和汇T。
1、从S向每个Xi连接一条容量为该点收入的有向边。
2、从Yi向T连接一条容量为该点支出的有向边。
3、如果一个实验i需要设备j,连接一条从Xi到Yj容量为无穷大的有向边。

统计出所有实验的收入只和Total,求网络最大流Maxflow,最大收益就是Total-Maxflow。对应的解就是最小割划分出的S集合中的点,也就是最后一次增广找到阻塞流时能从S访问到的顶点。

【建模分析】

定义一个割划分出的S集合为一个解,那么割集的容量之和就是(未被选的A集合中的顶点的权值 + 被选的B集合中的顶点的权值),记为Cut。
A集合中所有顶点的权值之和记为Total,那么Total - Cut就是(被选的A集合中的顶点的权值 - 被选的B集合中的顶点的权值),即为我们的目标函数,记为A。
要想最大化目标函数A,就要尽可能使Cut小,Total是固定值,所以目标函数A取得最大值的时候,Cut最小,即为最小割。

该问题的一般模型为最大权闭合图,相关讨论见《最小割模型在信息学竞赛中的应用》作者胡伯涛。


大概就是这样了。。下面代码:

//最小割——>最大流
#include
#include
#include
#include
#include
#include
#include
#define inf 0x7ffffff
using namespace std;
int n,m,cnt=1;
int w[2005],cost[2005],d[2005],head[2005],start=0,end=2001,ans=0;
struct edge{int to,next,flow;}e[100001];
void ini(int x,int y,int flow){
	e[++cnt].to=y;e[cnt].flow=flow;e[cnt].next=head[x];head[x]=cnt;
}
void insert(int x,int y,int flow){
	ini(x,y,flow);ini(y,x,0);
}
queueq;
bool bfs(){
	memset(d,-1,sizeof(d));
	q.push(start);d[start]=0;
	while(!q.empty()){
		int k=q.front();q.pop();
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(e[i].flow>0&&d[kk]==-1)
			{
				d[kk]=d[k]+1;
				q.push(kk);
			}
		}
	}
	return d[end]!=-1;
}
int dfs(int x,int f){
	if(x==end) return f;
	for(int i=head[x];i;i=e[i].next){
		int k=e[i].to;int a;
		if(d[k]==d[x]+1&&e[i].flow>0&&(a=dfs(k,min(e[i].flow,f))))
		{
			e[i].flow-=a;
			e[i^1].flow+=a;
			return a;
		}
	}
	return 0;
}
void dinic(){
	while(bfs()){
		int a;
		while(a=dfs(start,end))
			ans+=a;
	}
}
int main(){
	int tot=0;
	scanf("%d%d",&m,&n);
	int tmp;
	for(int i=1;i<=m;i++){
		scanf("%d",&tmp);
		tot+=tmp;
		insert(start,i,tmp);
		char ch;
		int x=0;
		while(scanf("%c",&ch)){
			if(ch==' '){
				insert(i,x+1000,inf);
				x=0;
				continue;
			}
			else if(ch==10||ch==13){
                insert(i,x+1000,inf);
                x=0;break;
			}
			else
				x=x*10+ch-'0';
		}
	}
	int x;
	for(int i=1;i<=n;i++){
		scanf("%d",&x);
		insert(i+1000,end,x);
	}
	dinic();
	printf("%d",tot-ans);
	return 0;
}



你可能感兴趣的:(网络流,最大流,费用流,最小割)