UVA 11082

这个题的建模方法非常妙

首先处理出每一行和每一列所有元素值的和

先开一个源点和一个汇点,源点连所有的行,所有的列连汇

源点到行的容量为该行元素和,列到汇点同理,每一行到每一列的容量为最大值20,这样跑一个最大流,行i到列j的流量即为答案i,j的值,最后如果有解源点和汇点相邻的边都应该是满流

例如这一行的元素和是16,源点流过来16,再把16分配到每一列去

但这样有个问题就是中间可能有0流

所以就预先先把每个位置填个1,在网络流的网络中相应修改一下容量就可以

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define INF (210000000)
#define fr(i,s,t) for (i=s;i<=t;i++)
using namespace std;
struct edge{
	int to,cap,flow;
};
vector <int> vec[200];
vector <edge> edges;
int n,m,a[200],b[200],res[50][50],d[50],cur[50],num;
void addedge(int x,int y,int cap){
	edges.push_back((edge){y,cap,0});
	edges.push_back((edge){x,0,0});
	int m=edges.size();
	vec[x].push_back(m-2);
	vec[y].push_back(m-1);
}
bool bfs(){
	memset(d,0,sizeof(d));
	int i,y,x;
	queue <int> q;
	q.push(0);
	while (!q.empty()){
		x=q.front(); q.pop();
		fr(i,0,vec[x].size()-1)
		if (edges[vec[x][i]].cap>edges[vec[x][i]].flow){//开始忘了这个判断,这样就会死循环了 
			y=edges[vec[x][i]].to;
			if (!y||d[y]) continue;
			d[y]=d[x]+1;
			q.push(y);
		}
	}
	return d[n+m+1];
}
int dfs(int x,int a){
	if (x==n+m+1||a==0) return a;
	int flow=0,f;
	for (;cur[x]<vec[x].size();cur[x]++){
		int i=vec[x][cur[x]];//弧的编号 
		if (d[x]+1==d[edges[i].to]&&(f=dfs(edges[i].to,min(a,edges[i].cap-edges[i].flow)))>0){
			edges[i].flow+=f;
			edges[i^1].flow-=f;
			flow+=f;
			a-=f;
			if (a==0) break;
		}
	}
	return flow;
}
void work(){
	printf("Matrix %d\n",++num);
	scanf("%d %d",&n,&m);
	int i,j,y;
	fr(i,0,n+m+1) vec[i].clear();
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(res,0,sizeof(res));
	
	fr(i,1,n) scanf("%d",&a[i]); 
	fr(i,1,m) scanf("%d",&b[i]);
	for (i=n;i>=1;i--) a[i]-=a[i-1];
	for (i=m;i>=1;i--) b[i]-=b[i-1];
	
	fr(i,1,n) addedge(0,i,a[i]-m);
	fr(i,1,n) fr(j,n+1,n+m) addedge(i,j,19);
	fr(i,n+1,n+m) addedge(i,n+m+1,b[i-n]-n);
	
	while (bfs()) 
	  memset(cur,0,sizeof(cur)),dfs(0,INF);
	fr(i,1,n)
	  fr(j,0,vec[i].size()-1){
	  	y=edges[vec[i][j]].to-n;
	  	res[i][y]=edges[vec[i][j]].flow+1;
	  }
	fr(i,1,n){
	  fr(j,1,m)
	    printf("%d ",res[i][j]);
	  cout<<endl;
	}
	cout<<endl;
}
int main(){
	int nn;cin>>nn;
	while (nn--) work();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值