[codevs1222]信与信封问题(匈牙利)

xiaoxiao2021-02-28  80

题目描述 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,尽可能多地将信正确地装回信封。 输入描述 n文件的第一行是一个整数n(n≤100)。信和信封依次编号为1,2,…,n。 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 题意:告诉所有不能连的边,求剩下之中一定可以配对的边。看上去好像这道题有点诡异,其实就是匈牙利模板的一个变式。 思路: 输入:开了一个map数组,存的是可以相连的边(就是可能是配对的信与信封),map初值全部赋值为1,输入i,j的就赋值map[i,j]为0,表示i信不会在j中。 处理:先判断这个图是不是一个完美匹配(所有点都可以配对),如果不是完美匹配都可以直接输出none, 然后搜寻所有的边,删除当前存在的边,如果这条删了就不是完美匹配,说明,这条边的所连的i信是只有唯一的信封,即可以 确定此边存在,如果删了还是完美匹配,说明这条边只是可能,不能确定它一定存在的 代码如下 #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<cstdlib> #define maxn 105 using namespace std; int map[maxn][maxn];//数据范围比较小,直接开邻接矩阵 int num=0,vis[maxn],att[maxn]; int n,m; int can(int x) { for(int i=1;i<=n;i++) { if(vis[i]==0&&map[x][i]==1) { vis[i]=1; if(att[i]==0||can(att[i])) { att[i]=x; return 1; } } } return 0; } int perfect() { int  ans=0; for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(can(i))ans++; } memset(att,0,sizeof(att)); if(ans==n)return 1; return 0; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { map[i][j]=1; } } int a=1,b=1; while(a!=0&&b!=0) { scanf("%d%d",&a,&b); map[a][b]=0; } if(perfect()==0)printf("none");//不是完美匹配直接输出none else{ for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(map[i][j]==1) { map[i][j]=0;//删除边的操作 if(perfect()==0)//删除边就不是完美匹配,说明这条边是i的唯一边 { map[i][j]=1; num++; printf("%d %d\n",i,j);//题目要求i从小到大输出,所以直接从小到大找即可 } else { map[i][j]=1; } } } } if(num==0)printf("none"); } //noip水军一名,初次发文,如有不妥,请各位大佬指出
转载请注明原文地址: https://www.6miu.com/read-49294.html

最新回复(0)