二分图博弈
二分图博弈
什么是二分图博弈:
两个人博弈,每一次自己结束一定会轮到对方,并且自己某个状态选择后对方可以选择的状态是一个区间。
结论:
二分图博弈,作为起点的一方要是选择状态 \(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";
}
}
}