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;
}

 

posted on 2015-07-13 17:33  大树置林  阅读(197)  评论(0编辑  收藏  举报

导航