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

本文针对一个特定的数学问题——矩阵重构挑战展开讨论。该问题要求根据矩阵的行和及列和来判断原始矩阵是否存在及其唯一性。文章首先阐述了问题背景,随后详细介绍了两种解决方法:一是采用网络流算法进行求解,二是通过矩阵DP判断答案的唯一性。
摘要由CSDN通过智能技术生成

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


转载于:https://www.cnblogs.com/mypsq/p/4348173.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值