二分图博弈

二分图博弈

什么是二分图博弈:

两个人博弈,每一次自己结束一定会轮到对方,并且自己某个状态选择后对方可以选择的状态是一个区间。

结论:

二分图博弈,作为起点的一方要是选择状态 \(P\),若状态 \(P\) 一定在最大匹配上,则先手必胜,反之,先手必败。
这里的胜败表示的是“不能继续转移的人”失败。

考虑怎么找 \(P\)(甚至是 \(P\) 的集合),显然就是总共的点减去非必要点。考虑非必要点怎么求。
直接做一次二分图匹配,将匹配边定为从左向右,其他边视为从右向左连边。
然后对所有非匹配点跑 dfs,能到达的都是非必要的。

模板:


#include<bits/stdc++.h>
using namespace std;
bool vis[10005],viss[10005];
int rev[10005];
vector<int>mp[10005];
vector<int>mpp[10005];
bool get(int x){
	for(int i=0;i<mp[x].size();i++){
		int to=mp[x][i];
		if(vis[to]){
			continue; 
		}
		vis[to]=1;
		if(!rev[to]||get(rev[to])==1){
			rev[to]=x;
			return 1;
		}
	}
	return 0;
}

void dfs(int x,int fa){
	if(vis[x]){
		return;
	}
	vis[x]=1;
	for(int i=0;i<mpp[x].size();i++){
		int to=mpp[x][i];
		if(to==fa){
			continue;
		}
		dfs(to,x);
	}
}
int main(){
	ios::sync_with_stdio(false);
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin >> u >> v;
		mp[u].push_back(v+n);
		mp[v+n].push_back(u);
	}
	for(int i=1;i<=n;i++){
		for(int j=n+1;j<=2*n;j++){
			vis[j]=0;
		}
		viss[i]=get(i);
	}
	for(int i=n+1;i<=2*n;i++){
		if(rev[i]){
			mpp[i].push_back(rev[i]);
		}
		for(int j=0;j<mp[i].size();j++){
			int to=mp[i][j];
			if(to==rev[i]){
				continue;
			}
			mpp[to].push_back(i);
		}
	}
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++){
		if(vis[i]||viss[i]){
			continue;
		}
		dfs(i,i);
	}
	bool win=0;
	for(int i=1;i<=n;i++){
		if(vis[i]==0){
			cout<<"Slavko\n";
		}
		else{
			cout<<"Mirko\n";
			
		}
	}
}

posted @ 2023-11-06 17:05  铃狐sama  阅读(17)  评论(0编辑  收藏  举报