loj雅礼集训 2017 Day2」棋盘游戏(二分图博弈之最大匹配必须点)

之前看了下二分图博弈,觉得理解了

这道板子题,我居然又困惑了好久!!真想抽自己~


明显看出这是一张二分图,我们用 i + j i+j 的奇偶性分类

接下来就可以套二分图博弈的板子了

(注意,下面是把先选择第一步棋子放哪里的人认为是先手,也就是Alice)

. : \color{Red}Ⅰ.结论一:

若先手选择的点可能不在最大匹配中必胜

若先手选择的点一定在最大匹配中必败

我写的理论证明,感觉还行

, 所以现在知道一个点一定在最大匹配中,先跑网络流

s s , 若s到这个点满流且残量网络中不存在s到这个点的路径,一定是必须点

, \color{Red}此时是必败的,否则必胜

, 当然先手选的点可以在二分图左边,也可以在二分图右边

t s , 所以再建图从t向s跑最大流,相同方式判断。

#include <bits/stdc++.h>
using namespace std;
#define id(x,y) (x-1)*m+y
const int maxn=2e5+10;
const int inf=1e9;
int n,m,s,t,yin[maxn];
int dis[maxn];
char a[109][109];
struct edge{
	int to,nxt,flow;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow){
	d[++cnt]=(edge){v,head[u],flow},head[u]=cnt;
	d[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
bool bfs(int s,int t)
{
	memset(dis,0,sizeof(dis));
	dis[s]=1;
	queue<int>q; q.push( s );
	while( !q.empty() )
	{
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v=d[i].to;
			if( d[i].flow&&dis[v]==0 )
			{
				dis[v]=dis[u]+1;
				if( v==t )	return true;
				q.push( v );
			}
		}
	}
	return false;
}
int dinic(int u,int t,int flow)
{
	if( u==t )	return flow;
	int res=flow;
	for(int i=head[u];i&&res;i=d[i].nxt )
	{
		int v=d[i].to;
		if( dis[v]==dis[u]+1&&d[i].flow)
		{
			int temp=dinic(v,t,min(res,d[i].flow) );
			if( temp==0 )	dis[v]=0;
			res-=temp;
			d[i].flow-=temp;
			d[i^1].flow+=temp;
		}
	}
	return flow-res;
}
typedef pair<int,int>p;
vector<p>vec;
int main()
{
	cin >> n >> m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		cin >> a[i][j];
	s=0,t=n*m+1;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		if( a[i][j]=='#' )	continue;
		if( (i+j)&1 )	add(s,id(i,j),1);
		else	add(id(i,j),t,1);
		yin[id(i,j)]=cnt-1;
		if( i>1&&a[i-1][j]=='.' )
		{
			if( (i+j)&1 )	add(id(i,j),id(i-1,j),1);
			else	add(id(i-1,j),id(i,j),1);
		}
		if( j>1&&a[i][j-1]=='.' )
		{
			if( (i+j)&1 )	add(id(i,j),id(i,j-1),1);
			else	add(id(i,j-1),id(i,j),1);			
		}
	}
	int ans=0;
	while( bfs(s,t) )	ans+=dinic(s,t,inf);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		if( a[i][j]=='#' )	continue;
		if( (i+j)&1 )
		{
			if( dis[id(i,j)]==0&&d[yin[id(i,j)]].flow==0 )	continue;//在最大匹配的充要条件 
			else	vec.push_back(p(i,j));//只要有可能不在最大匹配,就可以赢 
		}
	}
	for(int i=2;i<=cnt;i+=2)
		d[i^1].flow+=d[i].flow,d[i].flow=0;
	while( bfs(t,s) )	ans+=dinic(t,s,inf);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		if( a[i][j]=='#' )	continue;
		if( (i+j)&1 )	continue;
		else
		{
			if( dis[id(i,j)]==0&&d[yin[id(i,j)]+1].flow==0 )	continue;//在最大匹配的充要条件 
			else	vec.push_back(p(i,j));//只要有可能不在最大匹配,就可以赢 
		}
	}
	cout << vec.size() << endl;
	sort(vec.begin(),vec.end());
	for(int i=0;i<vec.size();i++)
		cout << vec[i].first << " " << vec[i].second << '\n';
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/108476370