【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;
}

浙公网安备 33010602011771号