P4055 [JSOI2009]游戏
分析:
每一次的移动可以看做两点之间连边,且不能重复经过一个点,如果走到一个点没有可以走的边了,则说明输了。
将棋盘黑白染色后连边即可得到一张二分图。
考虑先手应该放在哪个点会使得他自己赢。
先手放在非匹配点一定会赢。
因为对手只能走非匹配边,而先手在对手走了非匹配边之后走一条匹配边显然是最优的(非匹配边不一定有,而匹配边一定会有)
最后对手必输。
那么如果没有非匹配点怎么办?
先手必输。
因为此时是完美匹配,先手无论放在哪里,对手都会走一条匹配边把先手逼入绝路。
但这道题还要求输出第一次放的位置。
即如何求非匹配点:
1. match==0
2. 删去这个点后仍能找到一条增广路(即这个点是可要可不要的)
#include<bits/stdc++.h> using namespace std; #define N 10005 #define nn 105 #define ri register int int id[nn][nn],x[N],y[N],col[nn][nn],match[N],an[N],n,m,vis[N],fl[N]; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; char s[nn][nn]; vector<int> tot[2]; vector<int> e[N]; void init() { int cnt=0; col[0][1]=1; for(ri i=1;i<=n;++i){ int op=col[i-1][1]^1; for(ri j=1;j<=m;++j) id[i][j]=++cnt,x[id[i][j]]=i,y[id[i][j]]=j,col[i][j]=op,op^=1; } } int fll=0; bool dfs(int u) { if(vis[u]) return false; vis[u]=1; for(ri i=0;i<e[u].size();++i){ int v=e[u][i]; if(fl[v]) continue; if(!match[v] || dfs(match[v])){ match[v]=u; match[u]=v; return true; } } return false; } void add(int a,int b) { e[a].push_back(b); e[b].push_back(a); } int main() { scanf("%d%d",&n,&m); init(); for(ri i=1;i<=n;++i) scanf("%s",s[i]+1); for(ri i=1;i<=n;++i){ for(ri j=1;j<=m;++j) if(s[i][j]=='.'){ tot[col[i][j]].push_back(id[i][j]); if(col[i][j]) continue; for(ri k=0;k<=3;++k){ int xx=i+dx[k],yy=j+dy[k]; if(xx<1||yy<1||xx>n||yy>m || s[xx][yy]=='#') continue; add(id[i][j],id[xx][yy]); } } } int ans=0,cnt=0; for(ri i=0;i<tot[0].size();++i) memset(vis,0,sizeof(vis)),ans+=dfs(tot[0][i]); if(ans==max(tot[0].size(),tot[1].size())) printf("LOSE\n"); else{ printf("WIN\n"); for(ri i=1;i<=n;++i) for(ri j=1;j<=m;++j) if(s[i][j]=='.'){ int u=id[i][j]; if(!match[u]) an[++cnt]=u; else{ fl[u]=1; memset(vis,0,sizeof(vis)); int tmp=dfs(match[u]); if(tmp) an[++cnt]=u,match[u]=0; fl[u]=0; } } } for(ri i=1;i<=cnt;++i) printf("%d %d\n",x[an[i]],y[an[i]]); return 0; } /* 3 3 .## ... #.# 3 3 .#. ..# #.# */