P2055 [ZJOI2009]假期的宿舍 题解 暨 二分图最大匹配(匈牙利算法) 模板


匈牙利算法可以在\(O(n^3)\)的时间复杂度内求出二分图最大匹配.方法是:依次考虑每个左边点,对于每个人考虑每个右边点,找到第一个有边的右边点,如果没有匹配,就匹配,如果已经匹配,使用NTR技术让其匹配重新匹配.

二分图板子题,但莫名其妙很难写对.个人很反感这种用题意绕弯子来考人的题.

#include
#include
#include
using namespace std;
const int N=105;
int T,n,a,cnt,tot,sum;
int school[N],home[N],vis[N],p[N];
int head[N];
struct edge{
	int to,next;
} e[N*N];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
bool match(int k) {
	for (int i=head[k];i;i=e[i].next) {
		int v=e[i].to;
		if (vis[v]) continue;
		vis[v]=1;
		if (!p[v]||match(p[v])) {
			p[v]=k;
			return true;
		}
	}
	return false;
}
int main() {
	ios::sync_with_stdio(false);
	cin>>T;
	while (T--) {
		cnt=0,tot=0,sum=0;
		memset(head,0,sizeof(head));
		memset(p,0,sizeof(p));
		cin>>n;
		for (int i=1; i<=n; i++)
			cin>>school[i];
		for (int i=1; i<=n; i++) {
			cin>>home[i];
			if (home[i]==0&&school[i])
			add(i,i);
		}
		for (int i=1; i<=n; i++)
			if (!school[i]||(school[i]&&!home[i]))
				++tot;
		for (int i=1; i<=n; i++)
			for (int j=1; j<=n; j++) {
				cin>>a;
				if (a&&school[j])
					add(i,j);
			}
		for (int i=1; i<=n; i++) {
			if ((school[i]&&home[i]==0)||!school[i]) {
				memset(vis,0,sizeof(vis));
				if (match(i)) ++sum;
			}
		}
		if (sum==tot)
			cout<<"^_^"<