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