题目大意:给出一个由1到20组成的整数矩阵的每一行和每一列的和,构造这个矩阵。输出任意一个构造方案。
题目分析:将每一行视作一个点x,将每一列视作一个点y。对于矩阵中的每一个格子,都对应一个二元关系<x,y>,从x连一条有向弧到y,容量置为19。增加源点s和汇点t,对于每一个x,连一条从s到x的有向弧,容量置为对应的该行总和减去列数,对于每一个y,连一条从y到t的有向弧,容量置为对应的该列总和减去行数。对于这个寻找最大流,算法终止后,如果从s出发的每一条弧和到达t的每一条弧都是饱和的,那么每一个xi到yj的流量便是对应格子中的值减一;否则,无解。这道题,一定有解。 显然,这样建模是正确的。
代码如下:
# include<iostream> # include<cstdio> # include<cmath> # include<string> # include<vector> # include<list> # include<set> # include<map> # include<queue> # include<cstring> # include<algorithm> using namespace std; # define LL long long # define REP(i,s,n) for(int i=s;i<n;++i) # define CL(a,b) memset(a,b,sizeof(a)) # define CLL(a,b,n) fill(a,a+n,b) const double inf=1e30; const int INF=1<<30; const int N=1000; struct Edge { int fr,to,cap,flow; Edge(int _fr,int _to,int _cap,int _flow):fr(_fr),to(_to),cap(_cap),flow(_flow){} }; vector<Edge>edge; int id[50][50],r,c,a[25],b[25],f[50],p[50]; vector<int>G[50]; void init() { a[0]=b[0]=0; REP(i,0,r+c+2) G[i].clear(); edge.clear(); } void addEdge(int fr,int to,int cap) { edge.push_back(Edge(fr,to,cap,0)); edge.push_back(Edge(to,fr,0,0)); int m=edge.size(); id[fr][to]=m-2; G[fr].push_back(m-2); G[to].push_back(m-1); } void maxFlow(int s,int t) { while(1) { queue<int>q; CL(f,0); f[s]=INF; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); REP(i,0,G[u].size()){ Edge &e=edge[G[u][i]]; if(!f[e.to]&&e.cap>e.flow){ p[e.to]=G[u][i]; f[e.to]=min(f[u],e.cap-e.flow); q.push(e.to); } } if(f[t]) break; } if(!f[t]) break; for(int u=t;u!=s;u=edge[p[u]].fr){ edge[p[u]].flow+=f[t]; edge[p[u]^1].flow-=f[t]; } } } int main() { int T,cas=0; scanf("%d",&T); while(T--) { scanf("%d%d",&r,&c); init(); REP(i,1,r+1) scanf("%d",a+i); REP(i,1,c+1) scanf("%d",b+i); REP(i,1,r+1) addEdge(0,i,a[i]-a[i-1]-c); REP(i,1,r+1) REP(j,r+1,r+c+1) addEdge(i,j,19); REP(i,r+1,r+c+1) addEdge(i,r+c+1,b[i-r]-b[i-r-1]-r); maxFlow(0,r+c+1); printf("Matrix %d ",++cas); REP(i,1,r+1) REP(j,1,c+1) printf("%d%c",edge[id[i][j+r]].flow+1,(j==c)?' ':' '); if(T) printf(" "); } return 0; }