Uva11082_最大网络流(ISAP)

图论训练2——Uva11082

[链接]https://vjudge.net/problem/UVA-11082
  总结:本题是网络流的变型题,实际上涉及到的图论算法并不难。但是本题难点在于如何构造图,初看到本题时,一不注意可能就会将图中的点定义为矩阵中的一个元素,然而这样却行不通。考虑到图中的一个顶点可以连接多条边,而一条边却只能连接两个顶点,再看到矩阵中某一行与某一列有且仅有一个交点,就类似于一条边连接了两顶点。所以我们将图中的边定义为矩阵中的元素,顶点定义为矩阵的行与列。
  经过这样的构图后,我们得到的是一个行与列的二分图,引入源点s连接X顶点,汇点t连接Y顶点,X顶点与Y顶点之间的边的流量对应的则为矩阵中对应元素值。
  代码如下:

#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <vector>
#include <math.h>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=50,M=25;
struct Edge{
	int from,to,cap,flow;
	Edge(int a,int b,int c,int d){
		from=a,to=b,cap=c,flow=d;
	}
};
vector<Edge> edges;
vector<int> G[maxn];
int s,t,R,C,sumR[M],sumC[M];
void AddEdge(int from,int to,int cap){
	edges.push_back(Edge(from,to,cap,0));
	edges.push_back(Edge(to,from,0,0));
	int m=edges.size();
	G[from].push_back(m-2);
	G[to].push_back(m-1);
}
void build(void){
	scanf("%d%d",&R,&C);
	for(int i=1;i<=R;++i)
		scanf("%d",&sumR[i]);
	for(int i=1;i<=C;++i)
		scanf("%d",&sumC[i]);
	for(int i=R;i>=1;--i) sumR[i]-=sumR[i-1];
	for(int i=C;i>=1;--i) sumC[i]-=sumC[i-1];
	edges.clear();
	for(int i=0;i<maxn;++i) G[i].clear();
	s=0,t=maxn-1;
	for(int i=1;i<=R;++i)							
		AddEdge(s,i,sumR[i]-C);			//此处减去R和C是为了将矩阵元素的范围定义为0-19,以简化求解 
	for(int i=1;i<=C;++i)
		AddEdge(i+M,t,sumC[i]-R);
	for(int i=1;i<=R;++i)
		for(int j=1;j<=C;++j)
			AddEdge(i,j+M,19);					
	//		建图 
}
int num[maxn+1],cur[maxn],p[maxn],d[maxn];
int visited[maxn];
void bfs(void){						//反向bfs 
	memset(visited,0,sizeof(visited));
	visited[t]=1,d[t]=0;
	queue<int> q;q.push(t);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<G[u].size();++i){
			Edge& e=edges[G[u][i]^1];
			if(!visited[e.from]&&e.cap>e.flow){
				d[e.from]=d[u]+1;
				visited[e.from]=1;
				q.push(e.from);
			}
		}
	}
}
int Augment(void){				//增广 
	int a=1<<29,x=t;
	while(x!=s){
		Edge& e=edges[p[x]];
		a=min(a,e.cap-e.flow);
		x=e.from;
	}
	x=t;
	while(x!=s){
		edges[p[x]].flow+=a;
		edges[p[x]^1].flow-=a;
		x=edges[p[x]].from;
	}
	return a;
}
int isap(void){										//ISAP主过程 
	int flow=0;
	memset(num,0,sizeof(num));
	bfs();
	for(int i=0;i<maxn;++i) num[d[i]]++;
	memset(cur,0,sizeof(cur));
	int x=s;									
	while(d[s]<maxn){							
		if(x==t){
			flow+=Augment();
			x=s;
		}
		int ok=0;
		for(int i=cur[x];i<G[x].size();++i){
			Edge& e=edges[G[x][i]];
			if(e.cap>e.flow&&d[x]==d[e.to]+1){
				ok=1;
				p[e.to]=G[x][i];
				cur[x]=i;
				x=e.to;
				break;
			}
		}
			if(!ok){
				int m=maxn-1;
				for(int i=0;i<G[x].size();++i){
					Edge& e=edges[G[x][i]];
					if(e.cap>e.flow) m=min(m,d[e.to]);
				}													
				if(--num[d[x]]==0) break;
				num[d[x]=m+1]++;
				cur[x]=0;
				if(x!=s) x=edges[p[x]].from;
			}
		
	}
	return flow;
}
int main(void){
	int T;scanf("%d",&T);
	for(int kase=1;kase<=T;++kase){
		build();isap();
		printf("Matrix %d\n",kase);
		for(int i=1;i<=R;++i){						
			for(int j=1;j<G[i].size();++j){
				Edge& e=edges[G[i][j]];
				if(j!=1) printf(" %d",e.flow+1);
				else printf("%d",e.flow+1);
			}
			putchar('\n');
		}
		if(kase!=T) putchar('\n');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值