Loading

洛谷P2055 [ZJOI2009]假期的宿舍(二分图最大匹配)

二分图最大匹配问题。把需要找床(非本校学生以及本校不回家学生)作为左部点,本校学生的床(以序号i + n存储为点)作为右部点,遍历关系矩阵,对于本校不回家学生和他的床连双向边,需要床的学生(本校不回家学生以及外校学生)和认识他的本校学生(无论回不回家)的床连边,跑匈牙利 or 网络流 求出来二分图最大匹配,看值是否等于需要床的学生数。

注意及时memset和清除tot变量,以及临接表数组要开二倍和四倍的大小(存储双向边,以及点的个数是2 * n个)。

#include <iostream>
#include <cstring>
using namespace std;
int n, need, match[110];
bool school[110], home[110], rela[110][110], vis[110];
int head[110], ver[220], Next[220], tot = 0;
void add(int x, int y)
{
	ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
bool dfs(int x)
{
	for(int i = head[x], y; i; i = Next[i])
	{
		if(!vis[y = ver[i]])
		{
			vis[y] = 1;
			if(!match[y] || dfs(match[y])){
				match[y] = x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	freopen("data.txt", "r", stdin);
	ios::sync_with_stdio(false); 
	int t;
	cin >> t;
	while(t--)
	{
		tot = 0;
		memset(head, 0, sizeof(head));
		cin >> n;
		need = 0;
		memset(match, 0, sizeof(match));
		for(int i = 1; i <= n; i++)
		{
			cin >> school[i];
		}
		for(int i = 1; i <= n; i++)
		{
			cin >> home[i];
			if(!school[i] || school[i] && (!home[i])) need++;
		}
		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= n; j++)
			{
				cin >> rela[i][j];
				if(i == j && school[i] && (!home[i]))
				{
					add(i, i + n);
					add(i + n, i);
					continue;
				}
				if(rela[i][j])
				{
					if((school[i] && (!home[i]) || !school[i] ) && school[j])
					{
						add(i, j + n);
						add(j + n, i);
					}
				}
			}
		}
		for(int i = 1; i <= n; i++)
		{
			memset(vis, 0, sizeof(vis));
			if(!school[i] || school[i] && (!home[i])) 
			{
				if(dfs(i)) need--;
			}
		}
		if(!need)
		{
			cout << "^_^" << endl;
		}
		else cout << "T_T" << endl;
	}
	return 0;
}
posted @ 2021-02-18 12:58  脂环  阅读(31)  评论(0编辑  收藏  举报