【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;
}
posted @ 2011-08-14 09:37  Delostik  阅读(1138)  评论(0编辑  收藏  举报