来自codevs fzuoj
codevs
fzuoj
题目描述 Description
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,尽可能多地将信正确地装回信封。
输入描述 Input Description
n文件的第一行是一个整数n(n≤100)。信和信封依次编号为1,2,…,n。
n接下来的各行中每行有2个数i和j,表示第i封信肯定不是装在第j个信封中。文件最后一行是2个0,表示结束。
输出描述 Output Description
输出文件的各行中每行有2个数i和j,表示第i封信肯定是装在第j个信封中。请按信的编号i从小到大顺序输出。若不能确定正确装入信封的任何信件,则输出“none”。
样例输入 Sample Input
3
1 2
1 3
2 1
0 0
样例输出 Sample Output
1 1
数据范围及提示 Data Size & Hint
这题用的方法是二分图匹配和反证法
建模的过程就是把信与信封的匹配抽象成x点集和y点集的二分图匹配,如果信i能放入信封j,就在xi和yj之间连一条边
直接判断一封信是否仅能放入一个信封很困难。于是用反证的方法,如果去掉一条从xi出发,到yj的边,就不可能找到包含xi的匹配,那么xi只能匹配yj。
找不到包含xi的匹配,就是说,从xi出发,找不到增广路
#include<cstdio>
#include<cstring>
#define maxn 110
#define rg register
using namespace std;
int ans=0,n,x,y,p[maxn],cx[maxn],cy[maxn];
bool v[maxn],g[maxn][maxn];
bool dfs(int t)
{
if(t<=0) return false;
for(int i=1;i<=n;i++)
if(g[t][i]&&v[i]){
int &p=i;
v[p]=false;
if(cy[p]==-1||dfs(cy[p])){
cx[t]=p;
cy[p]=t;
return true;
}
}
return 0;
}
int main()
{
scanf("%d",&n);
for(register int i=1;i<=n;i++){
for(register int j=1;j<=n;j++){
g[i][j]=true;
}
}
while(scanf("%d%d",&x,&y)>0&&x!=0&&y!=0){
g[x][y]=false;
}
memset(cx,-1,sizeof(cx));
memset(cy,-1,sizeof(cy));
for(register int i=1;i<=n;i++){
if(cx[i]==-1){
memset(v,true,sizeof(v));
ans+=dfs(i);//二分图匹配,若都能匹配则可以
}
}
if(ans<n){//存在不能匹配的点
printf("none\n");
return 0;
}
bool flag=true;
for(rg int i=1;i<=n;i++){
int p=cx[i];
g[i][p]=false;//将这条边删去
cx[i]=-1;
cy[p]=-1;//将信从信封里取出来
memset(v,true,sizeof(v));
if(!dfs(i)){//如果不能匹配
printf("%d %d\n",i,p);
flag=false;
cx[i]=p;//把信放回去
cy[p]=i;
}
//如果二分图找到了一个新的匹配,就不必再把信放回信封
g[i][p]=true;//重新加上删除的边
}
if(flag) printf("none\n");
return 0;
}