1222 信与信封问题(二分图匹配)

解决一个信封匹配问题,通过匈牙利算法确定信与信封的正确配对,若完全匹配则进一步验证哪些配对是确定无疑的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述 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

思路:

 先跑匈牙利算法,判断是否能全部匹配
 如果不能匹配 那么直接输出“none”
 如果能匹配起来 那么再检验 哪个一定匹配正确
 可以删边来判断 删去一个匹配好的边 
 看看是否能全部匹配

代码:

#include<cstdio>
#include<iostream>
#define MAXN 110
using namespace std;
int f[MAXN][MAXN],n,pre[MAXN],pre2[MAXN];
bool vis[MAXN],flag;
inline void read(int&x) {
    int f=1;x=0;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=10*x+c-48,c=getchar();
    x=x*f;
}
inline int find(int u) {
    for(int i=1;i<=n;i++) {
        if(!f[u][i]&&!vis[i]) {
            vis[i]=true;
            if(!pre[i]||find(pre[i])) {
                pre[i]=u;
                return 1;
            }
        }
    }
    return 0;
}
inline void check() {
    for(int i=1;i<=n;i++) pre2[pre[i]]=i;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) vis[j]=false;
        f[i][pre2[i]]=1;
        pre[pre2[i]]=0;
        if(!find(i)) {
            printf("%d %d\n",i,pre2[i]);
            flag=true;
        }
        pre[pre2[i]]=i;
        f[i][pre2[i]]=0;
    }
}
int main() {
    int x,y;
    read(n);
    while(true) {
        read(x);read(y);
        if(!x&&!y) break;
        f[x][y]=true;
    }
    int ans=0;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) vis[j]=false;
        ans+=find(i);
    }
    if(ans!=n) {
        printf("none\n");
        return 0;
    }
    check();
    if(!flag) printf("none\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值