hdu4975 网络流解方程组(网络流+dfs判环或矩阵DP)

http://acm.hdu.edu.cn/showproblem.php?pid=4975

A simple Gaussian elimination problem.

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 579    Accepted Submission(s): 194

Problem Description

Dragon is studying math. One day, he drew a table with several rows and columns, randomly wrote numbers on each elements of the table. Then he counted the sum of each row and column. Since he thought the map will be useless after
he got the sums, he destroyed the table after that.

However Dragon‘s mom came back and found what he had done. She would give dragon a feast if Dragon could reconstruct the table, otherwise keep Dragon hungry. Dragon is so young and so simple so that the original numbers in the table are one-digit number (e.g.
0-9).

Could you help Dragon to do that?

Input

The first line of input contains only one integer, T(<=30), the number of test cases. Following T blocks, each block describes one test case.

There are three lines for each block. The first line contains two integers N(<=500) and M(<=500), showing the number of rows and columns.

The second line contains N integer show the sum of each row.

The third line contains M integer show the sum of each column.

Output

Each output should occupy one line. Each line should start with "Case #i: ", with i implying the case number. For each case, if we cannot get the original table, just output: "So naive!", else if we can reconstruct the table by more
than one ways, you should output one line contains only: "So young!", otherwise (only one way to reconstruct the table) you should output: "So simple!".

Sample Input

3
1 1
5
5
2 2
0 10
0 10
2 2
2 2
2 2

Sample Output

Case #1: So simple!
Case #2: So naive!
Case #3: So young!

题目意思很简单:就是给出一个矩阵的行和和列和,矩阵中的每个元素都是0-9,问原矩阵是否存在,是否唯一;

分析:网络流求解,如果最大流=所有元素的和则有解;利用残留网络判断是否唯一,方法有两种,第一种是深搜看看是否存在正边权的环,至少3个点构成的环,第二种是用矩阵dp,假如某行的i列元素<9,j列元素>0,而另一行的i列元素>0,j列元素<9,那么答案不是唯一的,因为主对角线的 两个元素可以增大1,而副对角线的两个元素可以减小1,可以明显看出有多个答案;

比赛时的程序:

#include"stdio.h"
#include"string.h"
#include"iostream"
#include"map"
#include"string"
#include"queue"
#include"stdlib.h"
#include"math.h"
#define M 1900
#define eps 1e-10
#define inf 100000000
using namespace std;
struct node
{
    int u,v,w,next;
}edge[600000];
int t,head[M],row[M],col[M],q[M],dis[M],work[M],use[M];
void init()
{
    t=0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
    edge[t].u=u;
    edge[t].v=v;
    edge[t].w=w;
    edge[t].next=head[u];
    head[u]=t++;
    edge[t].u=v;
    edge[t].v=u;
    edge[t].w=0;
    edge[t].next=head[v];
    head[v]=t++;
}
int bfs(int S,int T)
{
    int rear=0;
    memset(dis,-1,sizeof(dis));
    dis[S]=0;
    q[rear++]=S;
    for(int i=0;i<rear;i++)
    {
        for(int j=head[q[i]];j!=-1;j=edge[j].next)
        {
            int v=edge[j].v;
            if(edge[j].w&&dis[v]==-1)
            {
                dis[v]=dis[q[i]]+1;
                q[rear++]=v;
                if(v==T)
                    return 1;
            }
        }
    }
    return 0;
}
int dfs(int cur,int a,int T)
{
    if(cur==T)
        return a;
    for(int &i=work[cur];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(edge[i].w&&dis[v]==dis[cur]+1)
        {
            int tt=dfs(v,min(a,edge[i].w),T);
            if(tt)
            {
                edge[i].w-=tt;
                edge[i^1].w+=tt;
                return tt;
            }
        }

    }
    return 0;
}
int Dinic(int S,int T)
{
    int ans=0;
    while(bfs(S,T))
    {
        memcpy(work,head,sizeof(head));
        while(int tt=dfs(S,inf,T))
            ans+=tt;
    }
    return ans;
}
int DFS(int u,int f)
{
     use[u]=1;
     for(int &i=work[u];i!=-1;i=edge[i].next)//加&和复制的work数组
     {
          int v=edge[i].v;
          if(edge[i].w&&v!=f)
          {
               if(use[v])
                    return 1;
               if(DFS(v,u))
                    return 1;
          }
     }
     use[u]=0;
     return 0;
}
int judge(int n,int m)
{
    memset(use,0,sizeof(use));
    memcpy(work,head,sizeof(head));//当初加了个这东西就莫名其妙的过了,并且很省时
     for(int i=1;i<=n;i++)
     {
          if(DFS(i,i))
               return 1;
     }
     return 0;
}
int main()
{
    int n,m,i,j,kk=1;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        int r=0,c=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&row[i]);
            r+=row[i];
        }
        for(j=1;j<=m;j++)
        {
            scanf("%d",&col[j]);
            c+=col[j];
        }
        printf("Case #%d: ",kk++);
        if(r!=c)
        {
            printf("So naive!\n");
            continue;
        }
        int flag=0;
        for(i=1;i<=n;i++)
        {
            if(m*9<row[i])
                flag++;
        }
        for(i=1;i<=m;i++)
        {
            if(n*9<col[i])
                flag++;
        }
        if(flag)
        {
            printf("So naive!\n");
            continue;
        }
        init();
        for(i=1;i<=n;i++)
        {
             for(j=1;j<=m;j++)
             {
                  add(i,n+j,9);
             }
        }
        for(i=1;i<=n;i++)
          add(0,i,row[i]);
        for(j=1;j<=m;j++)
          add(j+n,m+n+1,col[j]);
        int ans=Dinic(0,m+n+1);
        if(ans<r)
        {
             printf("So naive!\n");
             continue;
        }
        if(judge(n,m))
        {
             printf("So young!\n");
             continue;
        }
        printf("So simple!\n");
    }
    return 0;
}

之后用矩阵dp做的:

#include"stdio.h"
#include"string.h"
#include"iostream"
#include"map"
#include"string"
#include"queue"
#include"stdlib.h"
#include"math.h"
#define M 1900
#define eps 1e-10
#define inf 1000000000
#define mod 2333333
using namespace std;
struct node
{
    int u,v,w,next;
}edge[600000];
int t,head[M],work[M],use[M],dis[M],mp[555][555],G[555][555],row[555],col[555];
void init()
{
    t=0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v,int w)
{
    edge[t].u=u;
    edge[t].v=v;
    edge[t].w=w;
    edge[t].next=head[u];
    head[u]=t++;
    edge[t].u=v;
    edge[t].v=u;
    edge[t].w=0;
    edge[t].next=head[v];
    head[v]=t++;
}
int bfs(int S,int T)
{
    memset(dis,-1,sizeof(dis));
    queue<int>q;
    dis[S]=0;
    q.push(S);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(edge[i].w&&dis[v]==-1)
            {
                dis[v]=dis[u]+1;
                q.push(v);
                if(v==T)
                    return 1;
            }
        }
    }
    return 0;
}
int dfs(int cur,int a,int T)
{
    if(cur==T)return a;
    for(int &i=work[cur];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(edge[i].w&&dis[v]==dis[cur]+1)
        {
            int tt=dfs(v,min(edge[i].w,a),T);
            if(tt)
            {
                edge[i].w-=tt;
                edge[i^1].w+=tt;
                return tt;
            }
        }
    }
    return 0;
}
int Dinic(int S,int T)
{
    int ans=0;
    while(bfs(S,T))
    {
        memcpy(work,head,sizeof(head));
        while(int tt=dfs(S,inf,T))
            ans+=tt;
    }
    return ans;
}
int judge(int n,int m)
{
    int k=0,i,j;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=m;j++)
        {
            G[i][j]=edge[k^1].w;
            k+=2;
        }
    }
    memset(mp,0,sizeof(mp));
    for(i=1;i<=n;i++)
    {
        if(row[i]==0||row[i]==9*m)continue;
        for(j=1;j<=m;j++)
        {
            if(col[j]==0||col[j]==9*n)continue;
            for(k=j+1;k<=m;k++)
            {
                int f1=0,f2=0;
                if(G[i][j]<9&&G[i][k]>0)
                {
                    if(mp[k][j])
                        return 1;
                    f1++;
                }
                if(G[i][j]>0&&G[i][k]<9)
                {
                    if(mp[j][k])
                        return 1;
                    f2++;
                }
                if(f1)mp[j][k]=1;
                if(f2)mp[k][j]=1;
            }
        }
    }
    return 0;
}
int main()
{
    int T,m,n,kk=1,i,j;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        int r=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&row[i]);
            r+=row[i];
        }
        int c=0;
        for(j=1;j<=m;j++)
        {
            scanf("%d",&col[j]);
            c+=col[j];
        }
        printf("Case #%d: ",kk++);
        if(c!=r)
        {
            printf("So naive!\n");
            continue;
        }
        int flag=0;
        for(i=1;i<=n;i++)
            if(9*m<row[i])
            flag++;
        for(j=1;j<=m;j++)
            if(9*n<col[j])
            flag++;
        if(flag)
        {
            printf("So naive!\n");
            continue;
        }
        init();
        int st=0;
        int sd=n+m+1;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                add(i,j+n,9);
            }
        }
        for(i=1;i<=n;i++)
            add(st,i,row[i]);
        for(j=1;j<=m;j++)
            add(j+n,sd,col[j]);
        int ans=Dinic(st,sd);
        if(ans!=r)
        {
            printf("So naive!\n");
            continue;
        }
        if(judge(n,m))
        {
            printf("So young!\n");
            continue;
        }
        printf("So simple!\n");
    }
    return 0;
}
时间: 2024-10-29 04:27:31

hdu4975 网络流解方程组(网络流+dfs判环或矩阵DP)的相关文章

UVA818-Cutting Chains(二进制枚举+dfs判环)

Problem UVA818-Cutting Chains Accept:393  Submit:2087 Time Limit: 3000 mSec  Problem Description What a ?nd! Anna Locke has just bought several links of chain some of which may be connected. They are made from zorkium, a material that was frequently

2018 计蒜之道复赛 贝壳找房魔法师顾问(并查集+dfs判环)

贝壳找房在遥远的传奇境外,找到了一个强大的魔法师顾问.他有 22 串数量相同的法力水晶,每个法力水晶可能有不同的颜色.为了方便起见,可以将每串法力水晶视为一个长度不大于 10^5105,字符集不大于 10^5105 的字符串.现在魔法师想要通过一系列魔法使得这两个字符串相同.每种魔法形如 (u,\ v),\ u,\ v \le 10^5(u, v), u, v≤105,可以将一个字符 uu改成一个字符 vv,并且可以使用无限次.出于种种原因,魔法师会强行指定这两个串能否进行修改. 若失败输出 -

CodeForces 711D Directed Roads (DFS判环+计数)

题意:给定一个有向图,然后你可能改变某一些边的方向,然后就形成一种新图,让你求最多有多少种无环图. 析:假设这个图中没有环,那么有多少种呢?也就是说每一边都有两种放法,一共有2^x种,x是边数,那么如果有环呢?假设x是这个连通块的边数, y是这个环的边数,那么就一共有2^x * (2 ^ y - 2) 种,减去这两种就是一边也不变,和所有的边都就变,这样就形成环了. 那么怎么计算呢?这个题的边很特殊,所以我们可以利用这个特征,我们在每个连通块时,用vis记录一下开始的父结点,用cnt记录每一个到

Codeforces 711 D. Directed Roads (DFS判环)

题目链接:http://codeforces.com/problemset/problem/711/D 给你一个n个节点n条边的有向图,可以把一条边反向,现在问有多少种方式可以使这个图没有环. 每个连通量必然有一个环,dfs的时候算出连通量中点的个数y,算出连通量的环中点的个数x,所以这个连通量不成环的答案是2^(y - x) * (2^x - 2). 最后每个连通量的答案相乘即可. 1 //#pragma comment(linker, "/STACK:102400000, 102400000

hdu4888 Redraw Beautiful Drawings 最大流+判环

hdu4888 Redraw Beautiful Drawings Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2007    Accepted Submission(s): 447 Problem Description Alice and Bob are playing together. Alice is crazy abou

拓扑排序判环

拓扑排序的核心就是每次找入度为0的点,进入输出队列 ,然后将与此点相连的节点入度减1重复做以上操作.当做n-1 次后还有点没进输出队列 那么这些点就是环上的 因为环上的各点入度都为1 没有0的 就不能更新.也就是说拓扑排序一遍之后,如果是DAG所有点都恰好入队一次如果有环,那么一定存在没有入队的点. 例题: Legal or NotTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Pr

HDUOJ--4888--Redraw Beautiful Drawings【isap】网络流+判环

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4888 题意:一个矩阵,限定每行行和.列和,每个格子数字不超过k,问矩阵是否存在,如存在判断有单解还是多解. 思路:之前多校的题目,那时候还不会网络流,现在A掉了,矩阵的建图模型,判断网络流是否可行只要判断最大流是否等于总行和或总列和即可,判环是看的别人的解题报告,方法是使用dfs查找残余网络中是否有还存在容量的弧形成了环,如果有,说明可以通过这个环改变容量网络内部的增广路方式,而源汇的流量是不会变的,就

hdu4975 网络流及‘删边法’判是否为唯一流

http://acm.hdu.edu.cn/showproblem.php?pid=4975 Problem Description Dragon is studying math. One day, he drew a table with several rows and columns, randomly wrote numbers on each elements of the table. Then he counted the sum of each row and column.

CodeForces 937D 936B Sleepy Game 有向图判环,拆点,DFS

题意: 一种游戏,2个人轮流控制棋子在一块有向图上移动,每次移动一条边,不能移动的人为输,无限循环则为平局,棋子初始位置为$S$ 现在有一个人可以同时控制两个玩家,问是否能使得第一个人必胜,并输出一个解,否则判断是否能平局 题解: 看到这个题首先我想到了强连通分量,但是事实证明求出强连通分量,缩点对解决问题没有什么帮助.... 能写一些看似正确的算法,但其实是假算法来的.. ........... 所以应该先分析策略,肯定是能赢就赢,不能赢就求平局,最后才算输 平局很好判断,有向图上,从$S$点