【JSOI 2009】游戏 Game

  http://www.zybbs.org/JudgeOnline/problem.php?id=1443

  此题竟然被改造出现在NOI2011的赛场上……

  半年前qz神牛在回宿舍的路上跟我吐槽这道题的捉法,但是被我忘干净了。NOI出现这道题我庆幸自己只是参加了同步赛,要不然就挂了。

  膜拜过qz和CLJ的题解之后搞定了这个题。

------------------------------Clavichord Orz----------------------------WJMZBMR Orz----------------------------------------

  我们想象棋盘被黑白染色,那么黑格的棋子只能往相邻的白格走,白格的棋子只能往相邻的黑格走。

  当对方走到一个白格(或黑格)时,如果上下左右有合法的黑格(或白格),那么我们就可以移动到这个格子。

  每个格子只能被经过一次。到这里我们可以想到二分图最大匹配。

  当先手将棋子放置在“一定是”最大匹配的格子时,后手可以顺着最大匹配走,一定是先手必败。

  当先手将棋子放置在其他位置时(不一定是最大匹配的格子),先手就可以顺着后手的最大匹配走,从而先手获胜。

  问题转化为在一个二分图中判断某点是否可以不在最大匹配中。

  我在求最大匹配的过程中使用sap增广,对于“一定在最大匹配中”的判断我使用的类似判断割边的方法,从源点和汇点分别进行dfs判断。程序跑到了624MS,惊讶…………

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <cmath>

#define inf 2147483647

#define num(i,j) ((i)*m+(j))

#define mm 400000

using namespace std;



int n,m,d[10010],vd[10010],map[101][101],source,sink;

bool vis[10010],match[10010];



struct EDGE{

	int pnt,cap;

	EDGE *pre,*ref;

	EDGE(){}

	EDGE(int _pnt,int _cap,EDGE *_pre):pnt(_pnt),cap(_cap),pre(_pre){}

}Edge[mm*2],*SP=Edge,*edge[mm];



inline void addedge(int a,int b){

	edge[a]=new(++SP)EDGE(b,1,edge[a]);

	edge[b]=new(++SP)EDGE(a,0,edge[b]);

	edge[a]->ref=edge[b],edge[b]->ref=edge[a];

}



int sap(int i,int flow){

	if(i==sink) return flow;

	int cur=0;

	for(EDGE *j=edge[i];j;j=j->pre)

		if(j->cap&&d[i]-d[j->pnt]==1){

			int tmp=sap(j->pnt,min(flow-cur,j->cap));

			j->ref->cap+=tmp,j->cap-=tmp,cur+=tmp;

			if(cur==flow) return cur;

		}

	if(!(--vd[d[i]])) d[source]=sink+1;

	if(d[source]>sink+1) return cur;

	vd[++d[i]]++;

	return cur;

}



void dfs(int i,int flag){

	vis[i]=true;

	if(((i%m+i/m)&1)==flag) match[i]=true;

	for(EDGE *j=edge[i];j;j=j->pre)

		if(j->cap==flag&&!vis[j->pnt]) dfs(j->pnt,flag);

}



int main(){

	freopen("game.in","r",stdin);

	freopen("game.out","w",stdout);

	scanf("%d%d",&n,&m);getchar();

	for(int i=0;i<n;i++,getchar())

		for(int j=0;j<m;j++)

			map[i][j]=getchar()=='.';

	source=n*m,sink=source+1;

	for(int i=0;i<n;i++)

		for(int j=0;j<m;j++)

			if(map[i][j])

				if((i+j)&1){

					addedge(source,num(i,j));

					if(i&&map[i-1][j]) addedge(num(i,j),num(i-1,j));

					if(j&&map[i][j-1]) addedge(num(i,j),num(i,j-1));

					if(i<n-1&&map[i+1][j]) addedge(num(i,j),num(i+1,j));

					if(j<m-1&&map[i][j+1]) addedge(num(i,j),num(i,j+1));

				}else addedge(num(i,j),sink);

	vd[0]=sink+1;

	while(d[source]<sink+1) sap(source,inf);

	memset(vis,false,sizeof(vis));dfs(source,1);

	memset(vis,false,sizeof(vis));dfs(sink,0);

	bool Win_Chance=false;

	for(int i=0;i<n*m;i++)

		if(match[i]){

			Win_Chance=true;

			break;

		}

	if(Win_Chance){

		printf("WIN\n");

		for(int i=0;i<n*m;i++)

			if(match[i]) printf("%d %d\n",(i/m)+1,(i%m)+1);

	}else printf("LOSE\n");

	return 0;

}

你可能感兴趣的:(game)