P4189信与信封问题
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 1s
问题描述
John先生晚上写了n封信,并相应地写了n个信封将信装好,准备寄出。
但是,第二天John的儿子Small John将这n封信都拿出了信封。
不幸的是,Small John无法将拿出的信正确地装回信封中了。
编程任务:
Small John所提供的n封信依次编号为1,2,…,n;且n个信封也依次编号为1,2,…,n。
假定Small John能提供一组信息:第i封信肯定不是装在信封j中。请编程帮助Small John,尽可能多地将信正确地装回信封。
输入格式
第一行是一个整数n(n≤100)。信和信封依次编号为1,2,…,n。
接下来的若干行,其中每行有2个数i和j,表示第i封信肯定不是装在第j个信封中。
后一行是2个0,表示结束。
输出格式
若干行,其中每行有2个数i和j,表示第i封信肯定是装在第j个信封中。请按信的编号i从小到大顺序输出。
若不能确定正确装入信封的任何信件,则输出“none”。
样例输入
3
1 2
1 3
2 1
0 0
样例输出
1 1
来源 FJOI 2001
首先我很好奇为什么John大晚上不睡觉去写信,还写了好几封,这是有多不走心
其次,小John为什么随便翻他爸爸的信,这个国家没点隐私权的么
题解
这道题目的解法很暴力
真的很暴力
建图
由于题目给的是否定的答案,但是我们通过一定的否定答案很难推导出肯定答案
所以我们就反向建图
哪条边是否定的,我们就删去这条边
而没有被提到的,就建起来
这样我们就得到了一个 某封信 可能装在 某个信封 的图
解题
首先,我们把信放在一遍,把信封放在一边,一个二分图就有了
其次,一个信封只能装一封信,那么装完信之后我们得到的集合就是一个配对
所以这道题目要用到最大匹配
易得:
假如某一个对应关系是必须的(即可以确定某一封信必须装进某一个信封),那么如果删去这条边,就不能找到一个配对使得每一封信都能够装进一个信封
但假如这个关系不是必须的,那么删掉了,每封信都还是可以在不冲突的情况下全部装进信封里头
也就是说,如果删去某条边之后,最大匹配的边数不为n(信的封数),那么这条边就是必须的,于是就可以输出了
所以我们只需要从小到大枚举每一条边,删掉这条边之后就跑一次最大匹配,如果最大匹配的边数不为n,那么这条边就是必须的
例如
有信1 2 3,信封4 5 6
1-5 2-5 3-5
1-4 2-4 3-4
1-6
那么如果删去1-6,得到的最大匹配无论如何无法达到3
所以1一定是装在6信封里头
但是其它的边管你怎么删,得到的最大匹配都是3
所以其它任意一条边都是必须的
注意
①删边是在原图上删去一条边,也就是说,删去下一条边的时候,这条边是存在的
②题目可能在一开始就是无解的,即一开始就无法保证能够把每一封信装进每一个信封,所以要先跑一次
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,fath[123];
bool went[123],flag=0;
bool maps[123][123];
bool find(int s)
{
for(int e=1;e<=n;e++)
if(maps[s][e]&&!went[e])
{
went[e]=1;
if(!fath[e]||find(fath[e]))
{
fath[e]=s;
return 1;
}
}
return 0;
}
int FD()
{
memset(fath,0,sizeof(fath));
int res=0;
for(int i=1;i<=n;i++)
{
memset(went,0,sizeof(went));
if(find(i))res++;
}
return res;
}
int main()
{
int s,e;
memset(maps,1,sizeof(maps));
scanf("%d",&n);
while(scanf("%d%d",&s,&e)!=EOF)maps[s][e]=0;
if(FD()!=n){printf("none");return 0;}
for(int s=1;s<=n;s++)
for(int e=1;e<=n;e++)
if(maps[s][e])
{
maps[s][e]=0;
if(FD()!=n){printf("%d %d\n",s,e);maps[s][e]=1;flag=1;break;}
maps[s][e]=1;
}
if(!flag)printf("none");
return 0;
}