图论训练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;
}