[关键字]:图论 网络流
[题目大意]:给出一个矩阵没行和和每列的和,并给出每个格子的上下界,求出一个合法的矩阵。
//============================================================================================================
[分析]:就是首先建立一个图,源点到每个行的边的容量上下界都为该行的和,每列到汇点连一条容量上下界都为该列和的边,每行的点到每列的点连一条容量上下界为这一行和这一列所代表的格子的上下界。然后对着个图判断它是否有最大流并求出。对一个图求容量有上下界的最大流的方法是:
1、新建一个图该图中有
(u,v)((u,v)存在与原图的边集中):容量c[u][v]=up[u][v]-low[u][v]
(S,u)(S是新图源点,u是任意一个原图中的点):容量c[S][u]=in[u](u的入边的下界和)
(u,T)(T是新图汇点,u是任意一个原图中的点):容量c[u][T]=out[u](u的出边的下界和)
(t,s)(t、s是原图的源汇点):容量c[t][s]=INF
2、求出该图的最大流,如果等于所有边的下界和就说明有解否则无解。
3、如果有解着在当前基础上继续再去掉(t,s)以及它的反向边因为此时反向边可能有流量,在求一边最大流这时的流量+下界就是原图的流量。
[代码]:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MAXN=300; const int INF=0x7fffffff; int n,m,test,s,t,S,T,N; int in[MAXN],out[MAXN]; int num[MAXN],h[MAXN],c[MAXN][MAXN],f[MAXN][MAXN]; int up[MAXN][MAXN],low[MAXN][MAXN],row[MAXN],col[MAXN]; void Init() { scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) scanf("%d",&row[i]); for (int i=1;i<=m;++i) scanf("%d",&col[i]); for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) up[i][j+n]=INF; int temp,x,y,z; char ch; scanf("%d",&temp); while (temp--) { scanf("%d %d %c %d",&x,&y,&ch,&z); //printf("%d %d %c %d\n",x,y,ch,z); if (x!=0 && y==0) for (int i=1;i<=m;++i) { if (ch=='=') up[x][i+n]=low[x][i+n]=z; if (ch=='<') up[x][i+n]=min(up[x][i+n],z-1); if (ch=='>') low[x][i+n]=max(low[x][i+n],z+1); } if (x==0 && y!=0) for (int i=1;i<=n;++i) { if (ch=='=') up[i][y+n]=low[i][y+n]=z; if (ch=='<') up[i][y+n]=min(up[i][y+n],z-1); if (ch=='>') low[i][y+n]=max(low[i][y+n],z+1); } if (x==0 && y==0) for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) { if (ch=='=') up[i][j+n]=low[i][j+n]=z; if (ch=='<') up[i][j+n]=min(up[i][j+n],z-1); if (ch=='>') low[i][j+n]=max(low[i][j+n],z+1); } if (x!=0 && y!=0) { if (ch=='=') up[x][y+n]=low[x][y+n]=z; if (ch=='<') up[x][y+n]=min(up[x][y+n],z-1); if (ch=='>') low[x][y+n]=max(low[x][y+n],z+1); } } s=0,t=n+m+1; for (int i=1;i<=n;++i) low[s][i]=up[s][i]=row[i]; for (int i=1;i<=m;++i) low[i+n][t]=up[i+n][t]=col[i]; /*for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) printf("%d %d %d %d\n",i,j,up[i][j+n],low[i][j+n]);*/ } int Find(int u,int flow) { if (u==T) return flow; int temp=flow,pos=N-1; for (int i=0;i<N;++i) { if (h[u]==h[i]+1 && c[u][i]>0) { int F=Find(i,min(temp,c[u][i])); c[u][i]-=F,c[i][u]+=F; f[u][i]+=F,f[i][u]-=F; temp-=F; if (temp==0 || h[S]==N) return flow-temp; } if (c[u][i]>0 && h[i]<pos) pos=h[i]; } if (temp==flow) { --num[h[u]]; if (num[h[u]]==0) h[S]=N; else { h[u]=pos+1; ++num[h[u]]; } } return flow-temp; } int SAP() { int sum=0; memset(h,0,sizeof(h)); memset(num,0,sizeof(num)); num[0]=N; while (h[S]<N) sum+=Find(S,INF);//,printf("%d\n",sum); return sum; } void Solve(int s,int t,int tot) { S=tot,T=tot+1; int tflow=0; for (int i=0;i<tot;++i) for (int j=0;j<tot;++j) { c[i][j]=up[i][j]-low[i][j]; in[j]+=low[i][j]; out[i]+=low[i][j]; tflow+=low[i][j]; } for (int i=0;i<tot;++i) { c[S][i]=in[i]; c[i][T]=out[i]; } c[t][s]=INF; N=tot+2; /*&for (int i=0;i<N;++i) for (int j=0;j<N;++j) printf("%d %d %d\n",i,j,c[i][j]);*/ int ans=SAP(); //printf("%d %d\n",ans,tflow); if (ans!=tflow) {printf("IMPOSSIBLE\n");return;} c[s][t]=c[t][s]=0; SAP(); for (int i=1;i<=n;++i) { for (int j=1;j<=m;++j) printf("%d ",f[i][j+n]+low[i][j+n]); printf("\n"); } } void Perpare() { memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(c,0,sizeof(c)); memset(f,0,sizeof(f)); memset(up,0,sizeof(up)); memset(low,0,sizeof(low)); } int main() { freopen("in","r",stdin); freopen("out","w",stdout); scanf("%d",&test); while (test--) { Perpare(); Init(); Solve(s,t,n+m+2); printf("\n"); } return 0; }