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;
}
时间: 2024-11-05 13:44:00

HDU 4888 Redraw Beautiful Drawings(网络流求矩阵的解)的相关文章

HDU 4888 Redraw Beautiful Drawings 网络流 建图

题意: 给定n, m, k 下面n个整数 a[n] 下面m个整数 b[n] 用数字[0,k]构造一个n*m的矩阵 若有唯一解则输出这个矩阵,若有多解输出Not Unique,若无解输出Impossible 思路:网络流,,, n行当成n个点,m列当成m个点 从行-列连一条流量为k的边,然后源点-行连一条a[i]的边, 列-汇点 流量为b[i] 瞎了,该退役了 T^T #include<stdio.h> #include<string.h> #include<iostream&

hdu 4888 Redraw Beautiful Drawings 网络流

题目链接 一个n*m的方格, 里面有<=k的数, 给出每一行所有数的和, 每一列所有数的和, 问你能否还原这个图, 如果能, 是否唯一, 如果唯一, 输出还原后的图. 首先对行列建边, 源点向行建边, 权值为这一行的和, 列向汇点建边, 权值为列和, 每一行向每一列建边, 权值为k, 因为上限是k. 然后跑一遍最大流, 如果最大流和所有行的和以及所有列的和相等, 那么可以还原. 判断是否唯一, 用判环的方式, 对图中每一个点dfs, 如果存在环, 说明原图不唯一. 原图每个格子的值等于k-这个格

HDU 4888 Redraw Beautiful Drawings(最大流+判最大流网络是否唯一)

Problem Description Alice and Bob are playing together. Alice is crazy about art and she has visited many museums around the world. She has a good memory and she can remember all drawings she has seen. Today Alice designs a game using these drawings

HDU 4888 Redraw Beautiful Drawings (2014-多校3-1002,最大流,判最大流有多解)

题目: http://acm.hdu.edu.cn/showproblem.php?pid=4888 题意: 给一个n*m的矩阵的n行之和和m列之和以及限制k,使用0-k的数字填充矩阵使得其行与列之和为给定值 如果不行则输出Impossible 如果有多解则输出Not Unique 如果有一解则输出Unique,并输出构造的矩阵 方法: 最大流,判最大流有多解 1.建图: 每一行一个点,每一列一个点 源点与第i个行点连边,权值为第i行之和 第j个列点与汇点连边,权值为第j行之和 第i个行点与第j

HDU 4888 Redraw Beautiful Drawings(2014 Multi-University Training Contest 3)

题意:给定n*m个格子,每个格子能填0-k 的整数.然后给出每列之和和每行之和,问有没有解,有的话是不是唯一解,是唯一解输出方案. 思路:网络流,一共 n+m+2个点   源点 到行连流量为 所给的 当前行之和.    每行 连到每一列 一条流量为  k的边,每列到汇点连 列和.如果流量等于总和则有解,反之无解(如果列总和不等于行总和也无解).  判断方案是否唯一 找残留网络是否存在长度大于2的环即可,有环说明不唯一. #include<cstdio> #include<cstring&

【HDU】4888 Redraw Beautiful Drawings 网络流【推断解是否唯一】

传送门:pid=4888">[HDU]4888 Redraw Beautiful Drawings 题目分析: 比赛的时候看出是个网络流,可是没有敲出来.各种反面样例推倒自己(究其原因是不愿意写暴力推断的).. 首先是简单的行列建边.源点向行建边.容量为该行元素和,汇点和列建边.容量为该列元素和.全部的行向全部的列建边,容量为K. 跑一次最大流.满流则有解,否则无解. 接下来是推断解是否唯一. 这个题解压根没看懂.还是暴力大法好. 最简单的思想就是枚举在一个矩形的四个端点.设A.D为主对角

hdu 4888 Redraw Beautiful Drawings(最大流,判环)

http://acm.hdu.edu.cn/showproblem.php?pid=4888 加入一个源点与汇点,建图例如以下: 1. 源点 -> 每一行相应的点,流量限制为该行的和 2. 每一行相应的点 -> 每一列相应的点,流量限制为 K 3. 每一列相应的点 -> 汇点,流量限制为该列的和 求一遍最大流,若最大流与矩阵之和相等,说明有解,否则无解.推断唯一解,是推断残量网络中是否存在一个长度大于2的环.若存在说明有多解,否则有唯一解,解就是每条边行i->列j的流量. #inc

hdu - 4888 - Redraw Beautiful Drawings(最大流)

题意:给一个N行M列的数字矩阵的行和以及列和,每个元素的大小不超过K,问这样的矩阵是否存在,是否唯一,唯一则求出各个元素N(1 ≤ N ≤ 400) , M(1 ≤ M ≤ 400), K(1 ≤ K ≤ 40). 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4888 -->>建图: 1)超级源S = 0,超级汇T = N + M + 1: 2)S到每个行和各连一条边,容量为该行行和: 3)每个行和到每个列和各连一条边,容量为K: 4)每个列和

hdu 4888 Redraw Beautiful Drawings

题目是一个矩阵,每行每列的数字的和都有一个上限,问是否存在可行方案,并且可行方案是否唯一. 第一问比较简单,行列建图,s到每个行节点容量为该行上限,每个列节点连接到t,容量为该列的上限,求最大流,如果满流则有可行方案.第二问就是判断最大流是否唯一,就是在原图中找一个环(经过一条边后不能马上走反向边),环上的边cap-flow都大于0.如果有这个环,那么不唯一,否则唯一.因为流量为k的两个流量图的差别肯定是一个个的环,否则流量不相同,只要按照这个环进行流量的重新分配就可以找到另一个方案. #inc