HDU 4888 Redraw Beautiful Drawings (最大流唯一性判定 Dinic)

91 篇文章 1 订阅

Redraw Beautiful Drawings

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 4410    Accepted Submission(s): 1314


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 in her memory. First, she matches K+1 colors appears in the picture to K+1 different integers(from 0 to K). After that, she slices the drawing into grids and there are N rows and M columns. Each grid has an integer on it(from 0 to K) representing the color on the corresponding position in the original drawing. Alice wants to share the wonderful drawings with Bob and she tells Bob the size of the drawing, the number of different colors, and the sum of integers on each row and each column. Bob has to redraw the drawing with Alice's information. Unfortunately, somtimes, the information Alice offers is wrong because of Alice's poor math. And sometimes, Bob can work out multiple different drawings using the information Alice provides. Bob gets confused and he needs your help. You have to tell Bob if Alice's information is right and if her information is right you should also tell Bob whether he can get a unique drawing.
 
Input
The input contains mutiple testcases.
For each testcase, the first line contains three integers N(1 ≤ N ≤ 400) , M(1 ≤ M ≤ 400) and K(1 ≤ K ≤ 40).
N integers are given in the second line representing the sum of N rows.
M integers are given in the third line representing the sum of M columns.
The input is terminated by EOF.
 
Output
For each testcase, if there is no solution for Bob, output "Impossible" in one line(without the quotation mark); if there is only one solution for Bob, output "Unique" in one line(without the quotation mark) and output an N * M matrix in the following N lines representing Bob's unique solution; if there are many ways for Bob to redraw the drawing, output "Not Unique" in one line(without the quotation mark).
 
Sample Input
  
  
2 2 4 4 2 4 2 4 2 2 2 2 5 0 5 4 1 4 3 9 1 2 3 3
 
Sample Output
  
  
Not Unique Impossible Unique 1 2 3 3
 
Author
Fudan University
 
Source
2014 Multi-University Training Contest 3

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4888

题目大意:给一个n*m的矩阵的第i行的和,第j列的和(1 <= i <= n, 1 <= j <= m),矩阵中每个元素不超过k,问这样的矩阵是否存在,存在的话是否唯一

题目分析:先把问题转换为网络流模型,从源点向行连边容量为行和,从每个行向每个列连边容量为k,从列向汇点连边容量为列和,判断存在只需要跑最大流,看最大流是否等于矩阵中所有元素的和,这样做其实相当于把n行的每个值通过不超过k的变换变成m列,判断唯一性只需在残余网络中找点数大于2的环即可,由于这个图的特殊性,因为到判断唯一性这步时显然已经有解,即源汇点的弧都满流,剩下的其实是个行到列的二分图,所以实际的环至少包含4个点且是X型的环,这样做的原因是如果存在这样的环,弧的流量就可以通过这个环交换,对样例一画个图就能看出来

第一个图为容量网络,第二个为一种解,明显看出包含一个环,第三个图是第二个图通过流量交换得到的,将弧13的流量变为2,剩下的2通过弧14再通过弧42和弧23到点3。
对于找环,直接DFS即可,这里需要两个标记变量,一个用来找当前的环,另一个用来标记肯定不在环中的点,如果一次DFS没找到环,那这次DFS过的点肯定不在环上,下次就不需要从这些点搜起了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int const INF = 0x3fffffff;
int n, m, k;
int src, sk, sum, num;
int cnt, head[1005];
int d[1005], ans[505][505];
bool vis[1005], mark[1005];

struct EDGE
{
    int to, nxt, cap;
}e[505 * 505 * 2];

void Init()
{
    cnt = 0;
    sum = 0;
    src = 0;
    sk = n + m + 1;
    memset(head, -1, sizeof(head));
}

void Add(int u, int v, int cap)
{
    e[cnt].to = v;
    e[cnt].cap = cap;
    e[cnt].nxt = head[u];
    head[u] = cnt ++;

    e[cnt].to = u;
    e[cnt].cap = 0;
    e[cnt].nxt = head[v];
    head[v] = cnt ++;
}

bool BFS()
{
    memset(d, -1, sizeof(d));
    queue <int> q;
    q.push(src);
    d[src] = 0;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int i = head[u]; i != -1; i = e[i].nxt)
        {
            int v = e[i].to;
            if(e[i].cap && d[v] == -1)
            {
                d[v] = d[u] + 1;
                q.push(v);
            }
        }
    }
    return d[sk] != -1;
}

int DFS(int u, int flow)
{
    if(u == sk || flow == 0)
        return flow;
    int res = 0;
    for(int i = head[u]; i != -1; i = e[i].nxt)
    {
        int v = e[i].to;
        if(e[i].cap && d[v] == d[u] + 1)
        {
            int mi = DFS(v, min(flow, e[i].cap));
            e[i].cap -= mi;
            e[i ^ 1].cap += mi;
            flow -= mi;
            res += mi;
            if(flow == 0)
                return res;
        }
    }
    if(!res)
        d[u] = -1;
    return res;
}

int Dinic()
{
    int ans = 0;
    while(BFS())
        ans += DFS(src, INF);
    return ans;
}

bool Judge(int u, int fa)
{
    mark[u] = true;
    for(int i = head[u]; i != -1; i = e[i].nxt)
    {
        int v = e[i].to;
        if(e[i].cap && v != fa)
        {
            if(vis[v])
                return true;
            vis[v] = true;
            if(Judge(v, u))
                return true;
            vis[v] = false;
        }
    }
    return false;
}

int main()
{
    while(scanf("%d %d %d", &n, &m, &k) != EOF)
    {
        Init();
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &num);
            Add(src, i, num);
            sum += num;
            for(int j = n + 1; j <= n + m; j++)
                Add(i, j, k);
        }
        for(int i = n + 1; i <= n + m; i++)
        {
            scanf("%d", &num);
            Add(i, sk, num);
        }
        int maxflow = Dinic();
        if(maxflow != sum)
            printf("Impossible\n");
        else
        {
            bool flag = true;
            memset(vis, false, sizeof(vis));
            memset(mark, false, sizeof(mark));
            for(int i = 1; i <= n && flag; i++)
            {
                if(!mark[i])
                {
                    vis[i] = true;
                    if(Judge(i, -1))
                        flag = false;
                    vis[i] = false;
                }
            }
            if(flag)
            {
                printf("Unique\n");
                memset(ans, 0, sizeof(ans));
                for(int i = 1; i <= n; i++)
                {
                    for(int j = head[i]; j != -1; j = e[j].nxt)
                    {
                        int v = e[j].to;
                        if(v > n && v <= n + m && (k - e[j].cap))
                            ans[i][v - n] = k - e[j].cap; 
                    }
                }
                for(int i = 1; i <= n; i++)
                    for(int j = 1; j <= m; j++)
                        printf("%d%c", ans[i][j], j == m ? '\n' : ' ');
            }
            else
                printf("Not Unique\n");
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值