洛谷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;
}