[JSOI2009]游戏

题目

给个有障碍物的(n imes m)棋盘,两个人轮流移动棋子,一个格子只能经过一次,不能移动的人算输;先手可以任选一个起点然后移动一格,问先手有多少个必胜的选法

思路

一人一步的东西,不是博弈论就是二分图了吧。。。毕竟上次被兔兔和蛋蛋坑了还是要记住的

按照套路,对棋盘黑白染色;对二分图求最大匹配,如果该二分图不存在完备匹配,那么先手必输,否则所有可能成为非匹配点的点都可以作为起点

因为从非匹配点出发,先手选择走匹配边走到一个匹配点,之后的路径就是走最大匹配的路径了,因为是偶数个点,所以最后一定是后手无路可走,可以证明一定后手无法走出这个最大匹配的路径,否则就会形成一条増广路

跑个网络流,用个DFS跑出所有非匹配点就行了,这个DFS类似求増广路的过程,具体见代码

Code

#include<bits/stdc++.h>
#define N 20005
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
const int inf = 10000000;
int n,m,s,t;
int a[105][105];
int dox[4]={0,1,0,-1},doy[4]={1,0,-1,0};
int dep[N],pp[N],up[N];
char c[105];
bool vis[N],lef[N];
struct Edge
{
	int next,to,flow;
}edge[N<<3];int head[N],cur[N],cnt=1;
void add_edge(int from,int to,int flow)
{
	edge[++cnt].next=head[from];
	edge[cnt].to=to;
	edge[cnt].flow=flow;
	head[from]=cnt;
}
void add(int from,int to,int flow)
{
	add_edge(from,to,flow);
	add_edge(to,from,0);
}
int id(int x,int y) {return (x-1)*m+y;}
bool bfs()
{
	memset(dep,0,sizeof(dep));
	queue<int> q;
	dep[s]=1; q.push(s);
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to;
			if(dep[v]||edge[i].flow<=0) continue;
			dep[v]=dep[u]+1;
			q.push(v);
			if(v==t) return true;
		}
	}
	return false;
}
int dfs(int rt,int rest)
{
	if(rt==t) return rest;
	int used=0;
	for(int i=head[rt];i&&used<=rest;i=edge[i].next)
	{
		int v=edge[i].to;
		if(dep[v]==dep[rt]+1&&edge[i].flow)
		{
			int k=dfs(v,Min(rest-used,edge[i].flow));
			if(!k) dep[v]=0;
			edge[i].flow-=k;
			edge[i^1].flow+=k;
			used+=k;
		}
	}
	return used;
}
int dinic()
{
	int ret=0;
	while(bfs()) ret+=dfs(s,inf);
	return ret;
}
void DFS(int rt,int lim)
{
	if(vis[rt]) return;
    vis[rt]=1;
    if(lef[rt]==lim) pp[rt]=1;
    for(int i=head[rt];i;i=edge[i].next) if(edge[i].flow==lim) DFS(edge[i].to,lim);
    
}
int main()
{
	scanf("%d%d",&n,&m);
	s=0;t=n*m+1;
	for(int i=1;i<=n;++i)
	{
		scanf("%s",c);
		for(int j=1;j<=m;++j) a[i][j]=(c[j-1]=='.');
	}
	int sum=0;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			if(a[i][j])
			{
				++sum;
				int fr=id(i,j);
				if((i+j)%2) add(s,fr,1),lef[fr]=1;
				else add(fr,t,1);
				
				if((i+j)%2) 
				for(int k=0;k<4;++k)
				{
					int dx=dox[k]+i,dy=doy[k]+j;
					if(dx<1||dy<1||dx>n||dy>m||!a[dx][dy]) continue;
					int to=id(dx,dy);
					add(fr,to,1);
				}
			}
		}
	}
	int ans=dinic()*2;
	if(sum==ans) { puts("LOSE"); return 0; }
	puts("WIN");
	DFS(s,1);
	memset(vis,0,sizeof(vis));
	DFS(t,0);
	
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=m;++j)
		if(pp[id(i,j)]&&a[i][j]) printf("%d %d
",i,j);
	return 0;
}
原文地址:https://www.cnblogs.com/Chtholly/p/11789457.html