信与信封 (二分图完备匹配)

John先生晚上写了n封信,并相应地写了n个信封将信装好,准备寄出。但是,第二天John的儿子Small John将这n封信都拿出了信封。不幸的是,Small John无法将拿出的信正确地装回信封中了。

 

将Small John所提供的n封信依次编号为12,…,n;且n个信封也依次编号为12,…,n。假定Small John能提供一组信息:第i封信肯定不是装在信封j中。请编程帮助Small John,尽可能多地将信正确地装回信封。

 

n文件的第一行是一个整数nn100)。信和信封依次编号为12,…,n

n接下来的各行中每行有2个数ij,表示第i封信肯定不是装在第j个信封中。文件最后一行是20,表示结束。

输出文件的各行中每行有2个数ij,表示第i封信肯定是装在第j个信封中。请按信的编号i从小到大顺序输出。若不能确定正确装入信封的任何信件,则输出“none”。

3

1  2

1  3

2  1

0  0

1   1



一、正确的匹配一定是一种完美匹配,(每封信都有一个信封可以装),所以不是完美匹配可以直接输出none进行剪枝。

二、如果存在一封信确定装在某个信封中,那么任何一种完美一定包含这个关系(因为关系唯一,所以为了达到完美匹配,必定会包含这个关系)。

通过一次匹配,找出所有的关系。当然,现在还不知道哪些边是确定的答案。接着测试每一条边,通过删除一条边并重新DFS搜索,如果找不到其它可匹配的,那么此条边一定是确定的(因为关系唯一)



#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 105
bool map[N][N],used[N];
int n,pre[N],pre1[N];
struct node
{
       int x,y;
};
bool cmp(node a,node b)
{
       return a.x<b.x;
}
int dfs(int u)
{
       int i;
       for(i=1;i<=n;i++)
              if(!used[i]&&map[u][i])
              {
                     used[i]=1;
                     if(pre[i]==-1||dfs(pre[i]))
                     {
                            pre[i]=u;
                            return 1;
                     }
              }
              return 0;
}
int dfs1(int u)
{
       int i;
       for(i=1;i<=n;i++)
              if(!used[i]&&map[u][i])
              {
                     used[i]=1;
                     if(pre1[i]==-1||dfs1(pre1[i]))
                     {
                            pre1[i]=u;
                            return 1;
                     }
              }
              return 0;
}
int main()
{
       while(cin>>n)
       {
              int a,b,i,j;
              memset(map,1,sizeof(map));
              while(scanf("%d%d",&a,&b),a&&b)
                     map[a][b]=0;
             
              memset(pre,-1,sizeof(pre));
              int ans=0;
              for(i=1;i<=n;i++)
              {
                     memset(used,false,sizeof(used));
                     if(dfs(i))
                            ans++;
              }
              if(ans!=n)
              {
                     cout<<"none"<<endl<<endl;
                     continue;
              }
      
              node edge[N];
              int cnt=0;
              for(i=1;i<=n;i++)
              {
                     map[pre[i]][i]=0;
                     ans=0;
                     memset(pre1,-1,sizeof(pre1));
                     for(j=1;j<=n;j++)
                     {
                            memset(used,false,sizeof(used));
                            if(dfs1(j))
                                   ans++;
                     }
                     if(ans!=n)
                     {
                            edge[cnt].x=pre[i];
                            edge[cnt++].y=i;
                     }
                     map[pre[i]][i]=1;
              }
              sort(edge,edge+cnt,cmp);
              if(cnt==0)
              {
                     cout<<"none"<<endl<<endl;
                     continue;
              }
              for(i=0;i<cnt;i++)
                     printf("%d %d\n",edge[i].x,edge[i].y);
              cout<<endl;
       }
       return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值