P2055 [ZJOI2009]假期的宿舍(二分图+建边)

题意:

学校放假了有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题。比如 A 和 B 都是学校的学生,A 要回家,而 C 来看B,C 与 A 不认识。我们假设每个人只能睡和自己直接认识的人的床。那么一个解决方案就是 B 睡 A 的床而 C 睡 B 的床。而实际情况可能非常复杂,有的人可能认识好多在校学生,在校学生之间也不一定都互相认识。我们已知一共有 n 个人,并且知道其中每个人是不是本校学生,也知道每个本校学生是否回家。问是否存在一个方案使得所有不回家的本校学生和来看他们的其他人都有地方住。

思路:

二分图匹配,建完边后直接dfs,把在学校的认识的连边,把不在学校的和在学校的认识的连边,在学校不回家的自己连自己,然后跑匈牙利把计算的结果和总共要在学校睡觉的人数比较,相等输出^_^否则输出T_T

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e2 + 10;
struct node {
    int v, net;
} no[N * 100];
int n, t, h[N], cnt, tot, ans;
int vis[N], march[N], sc[N], home[N];
void add(int u, int v) {
    no[cnt] = (node) {
        v, h[u]
    };
    h[u] = cnt++;
}
int dfs(int x) {
    for(int i = h[x]; ~i; i = no[i].net) {//x可以连的边
        int v = no[i].v;
        if(!vis[v]) {//如果没人占
            vis[v] = 1;
            if(!march[v] || dfs(march[v])) {//如果没匹配或者之前有可以变换匹配的
                march[v] = x;
                return 1;
            }
        }
    }
    return 0;
}
int main() {
    cin >> t;
    while(t--) {
        cin >> n;
        cnt = tot = 0, ans = 0;
        memset(h, -1, sizeof h);
        memset(march, 0, sizeof march);
        for(int i = 1; i <= n; i++)
            cin >> sc[i];
        for(int i = 1; i <= n; i++) {
            cin >> home[i];
            if(!home[i] && sc[i])//如果不回家并且是在校生
                add(i, i);
        }
        for(int i = 1; i <= n; i++)
            if(!sc[i] || (sc[i] && !home[i]))//如果不是在校生或者是不回家的在校生
                tot++;
        for(int x, i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) {
                cin >> x;
                if(x && sc[j])//如果认识并且认识的是在校生
                    add(i, j);
            }
        for(int i = 1; i <= n; i++)
            if((sc[i] && !home[i]) || !sc[i]) {//如果是不回家的在校生或者不是在校生
                memset(vis, 0, sizeof vis);
                ans += dfs(i);
            }
        ans == tot ? puts("^_^") : puts("T_T");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值