P2055 [ZJOI2009]假期的宿舍(二分图匹配,匈牙利算法)

题目描述

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

输入输出格式

输入格式:

第一行一个数 T 表示数据组数。接下来 T 组数据,每组数据第一行一个数n 表示涉及到的总人数。接下来一行 n 个数,第 i 个数表示第 i 个人是否是在校学生 (0 表示不是,1 表示是)。再接下来一行 n 个数,第 i 个数表示第 i 个人是否回家 (0 表示不回家,1 表示回家,注意如果第 i 个人不是在校学生,那么这个位置上的数是一个随机的数,你应该在读入以后忽略它)。接下来 n 行每行 n 个数,第 i 行第 j 个数表示 i 和 j 是否认识 (1 表示认识,0 表示不认识,第 i 行 i 个的值为 0,但是显然自己还是可以睡自己的床),认识的关系是相互的。

输出格式:

对于每组数据,如果存在一个方案则输出 “^_^”(不含引号) 否则输出“T_T”(不含引号)。(注意输出的都是半角字符,即三个符号的 ASCII 码分别为94,84,95)

输入输出样例

输入样例#1: 复制

1
3
1 1 0
0 1 0
0 1 1
1 0 0
1 0 0

输出样例#1: 复制

扫描二维码关注公众号,回复: 4490797 查看本文章
^_^

说明

对于 30% 的数据满足 1 ≤ n ≤ 12。

对于 100% 的数据满足 1 ≤ n ≤ 50,1 ≤ T ≤ 20。

多组数据!!!

思路:比较典型的二分图匹配算法,最关键的一点就是建立二分图,首先如果这个人是在校学生并且没有回家,那么他和他的床建立一条边,接下来我们将俩个认识的人与他们认识的人的床建立一条边,注意在这里我们只计算在校学生的床,非在校学生是没有床的,具体在代码里面我会解释一下,二分图建立完后我们来计算需要配对的总数,也就是总人数减去回家的人即是需要配对的对数,我们只需要判断需要配对的对数与配对的对数是不是一致就可以判断存不存在一个方案,因为是多组数据,所以每次都要初始化,下面来看代码

#include<iostream>
using namespace std;

const int maxn = 10010;
int cnt; 
int vis[maxn], head[maxn], match[maxn], isStudent[maxn], isGoHome[maxn];
struct node{
	int next;
	int to;
}edge[maxn];

void add(int u, int v) {
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}

int dfs(int u) {
	for (int i = head[u]; i != 0; i = edge[i].next) {
		int v = edge[i].to;
		if (!vis[v]) {
			vis[v] = 1;
			if (!match[v] || dfs(match[v])) {
				match[v] = u;
				return 1;
			}
		}
	}
	return 0;
}
int t, n;
int main() {
	cin >> t;
	while (t--) {
		cin >> n;
		cnt = 1;
		fill(head, head + maxn, 0);
		fill(match, match + maxn, 0);
		for (int i = 1; i <= n; i++) {
			cin >> isStudent[i];//是不是在校学生
		}
		for (int i = 1; i <= n; i++) {
			cin >> isGoHome[i];//是不是回家
			if (!isGoHome[i] && isStudent[i]) {//如果不回家并且是在校学生,那么他们和他们自己的床建立一条边
				add(i, i);
			}
		}
		int relation;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				cin >> relation;//i和j是不是认识
				if (relation && isStudent[j]) {//i和j认识并且j是在校学生,那么i和j的床连在一起,注意这里咱们没有连接i的床,因为i不是在校学生,没有床
					add(i, j);
				}
			}
		}
		int total = n;
		int num = 0;
		for (int i = 1; i <= n; i++) {
			if (isGoHome[i] && isStudent[i]) total--;//如果该在校学生回家,总人数减一,注意必须是在校生回家,如果将在校生去掉会wa,因为题目已经说了如果不是在校生那么输入的数据是随机数,在这里可能会出一些状况,所以我们多加一步判断
		}
		for (int i = 1; i <= n; i++) {
			if ((isStudent[i] && !isGoHome[i]) || !isStudent[i]) {//如果在校学生不回家或者不是在校学生,进行二分匹配
				fill(vis, vis + maxn, 0);
				if(dfs(i)) num++;
			}
		}
		if (num == total) {
			cout<<"^_^"<<endl;
		} else cout << "T_T" << endl; 
	}
}

二分匹配完毕

猜你喜欢

转载自blog.csdn.net/LanQiLi/article/details/84986016