HDU 4888 Redraw Beautiful Drawings(网络流求矩阵的解)
论文《为什么很多网络流问题总有整数解》http://diaorui.net/archives/189;
参考:http://www.cnblogs.com/yuiffy/p/3929369.html
题意:n*m的矩阵,给出每行的和以及每列的和,判断这样的矩阵是否存在,若存在,是否唯一;若唯一,输出解;
思路:网络流,最大流+判环。网络流常用于求多项式整数解。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int inf=0x7fffffff; bool found,walked[500010]; int t,nr,nc,k,sumr,sumc,ans; int r[500010],c[500010]; int an[5005][5005]; int head[500100],h[500010],g[500010]; //g[i]为层数为i的节点个数,h[i]为i点的层数 int d[500010],augc; //d记录当前弧,augc为增广路容量 int cnt,st,ed,flow,n,m; //n个点m条边,flow为最大流 struct node { int v,cap,next; }; node e[500010]; void add(int x,int y,int z) { e[cnt].v=y; e[cnt].cap=z; e[cnt].next=head[x]; head[x]=cnt++; e[cnt].v=x; e[cnt].cap=0; e[cnt].next=head[y]; head[y]=cnt++; } bool dfs(const int &x,const int &prex) //判环 { int biu=-1; walked[x]=true; for(int i=head[x];i!=-1;i=e[i].next) { if(e[i].v==prex) { biu=i;continue; } if(e[i].cap>0) { if(walked[e[i].v]) return true; if(dfs(e[i].v,x)) return true; } if(biu==-1) head[x]=e[i].next; //走了这条边没发现环,删边 else e[biu].next=e[i].next; biu=i; } walked[x]=false; return false; } void aug(const int &m) //dicnic { int i,mini,minh=n-1; int augco=augc; if(m==ed){ //当前点为汇点 found=true; flow+=augc; //增加流量 return; } for(i=d[m];i!=-1;i=e[i].next){ //寻找容许边 if(e[i].cap&&h[e[i].v]+1==h[m]) //如果残留量大于0且是容许边 { if(e[i].cap<augc) augc=e[i].cap;//如果残留量小于当前增广路流量,则更新增广路流量 d[m]=i; //把i定为当前弧 aug(e[i].v);//递归 if(h[st]>=n) return; //如果源点层数大于n,则返回 if(found) break; //找到汇点,跳出 augc=augco; //没找到就还原当前的流 } } if(!found){ for(i=head[m];i!=-1;i=e[i].next) { if(e[i].cap&&h[e[i].v]<minh){ minh=h[e[i].v]; mini=i; } } g[h[m]]--; if(!g[h[m]]) h[st]=n; h[m]=minh+1; d[m]=mini; g[h[m]]++; }else{ //修改残量 e[i].cap-=augc; //正向减 e[i^1].cap+=augc; //反向加 } } void farm() { int i,j,x,y,z; memset(head,-1,sizeof(head)); cnt=0; n=nc+nr+2; st=nc+nr+1; //源点 ed=nc+nr+2; //汇点 for(i=1;i<=nc;i++) add(st,i,c[i]); //源点到行连边,容量为该行的和 for(i=1;i<=nr;i++) add(nc+i,ed,r[i]);//列到汇点连边,容量为该列的和 for(i=1;i<=nc;i++) for(j=1;j<=nr;j++) add(i,j+nc,k); //行列间连边,容量为k memset(h,0,sizeof(h)); //层数,建分层图 memset(g,0,sizeof(g)); g[0]=n; flow=0; for(i=1;i<=n;i++) d[i]=head[i]; //当前弧初始化 while(h[st]<n){ augc=inf; //初始时增广路容量无穷大 found=false; aug(st); //从源点开始找 } if(flow!=sumr){ //达不到满流 ans=0; return; } for(i=1;i<=nr;i++) { int k=1; for(j=head[nc+i];j!=-1;j=e[j].next) { if(e[j].v==ed) continue; an[i][k++]=e[j].cap; int thenext=e[j].next; while(thenext!=-1&&e[thenext].v==ed) thenext=e[thenext].next; } } memset(walked,false,sizeof(walked)); for(i=nr;i>=1;i--) { if(dfs(i,-1))//将行数作为起始位置判环,若存在环,1~nr中必有点在环中 { ans=2; return; } } ans=1; } int main() { int i,j,cas; while(scanf("%d%d%d",&nr,&nc,&k)!=EOF) { sumr=sumc=0; for(i=1;i<=nr;i++) { scanf("%d",&r[i]); sumr+=r[i]; } for(i=1;i<=nc;i++) { scanf("%d",&c[i]); sumc+=c[i]; } ans=0; if(sumr==sumc) farm();//和不同,则无解 if(ans==0) printf("Impossible\n"); else if(ans!=1) { printf("Not Unique\n"); } else{ printf("Unique\n"); for(i=1;i<=nr;i++) { if(nc>=1) printf("%d",an[i][nc]); for(j=nc-1;j>=1;j--) { printf(" %d",an[i][j]); }printf("\n"); } } } return 0; }
有些梦想现在不去实现,以后就再也没机会了!!