1. 程式人生 > >BZOJ2437 NOI2011兔兔與蛋蛋(二分圖匹配+博弈)

BZOJ2437 NOI2011兔兔與蛋蛋(二分圖匹配+博弈)

  首先將棋盤黑白染色,不妨令空格處為黑色。那麼移動奇數次後空格一定處於白色格子,偶數次後空格一定處於黑色格子。所以若有某個格子的棋子顏色與棋盤顏色不同,這個棋子就是沒有用的。並且空格與某棋子交換後,棋子所在的格子改變使得該棋子與棋盤顏色不同,那麼該棋子也會變為無用棋子。那麼問題變為空格在棋盤上黑白格子交替移動,棋盤上有障礙物,走過的格子不能再走,不能移動者輸。

  顯然這是一個二分圖博弈。如果所有的最大匹配都包含起點,先手必勝,因為每次只需要沿匹配邊走即可,由增廣路定理不會出現沒邊走的情況。否則後手必勝,因為一旦起點不在最大匹配中,走了一條邊後後手變成先手。那麼每次我們只需要判斷空格位置是否處於剩餘圖的所有最大匹配中,也即判斷刪除該點後最大匹配是否會減少。這隻需要從被刪除點原本的匹配點出發找增廣路。判斷完每次操作後是否處於必勝態後只要看是否從必勝態走到了必勝態即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 42
char getc(){char c=getchar();while (c==10||c==13||c==32) c=getchar();return c;}
int gcd(int n,int
m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,k,X,Y,a[N][N],p[N*N],match[N*N],id[N][N],cnt,t,ans;
bool flag[N*N],tag[N*N],win[2010]; int wx[4]={0,0,1,-1},wy[4]={1,-1,0,0}; struct data{int to,nxt; }edge[N*N<<2]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} bool hungary(int k) { for (int i=p[k];i;i=edge[i].nxt) if (!flag[edge[i].to]&&!tag[edge[i].to]) { int x=edge[i].to; flag[x]=1; if (!match[x]||!tag[match[x]]&&hungary(match[x])) { match[x]=k,match[k]=x; return 1; } } return 0; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2437.in","r",stdin); freopen("bzoj2437.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { char c=getc(); a[i][j]=c=='O'; if (c=='.') X=i,Y=j; } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int k=0;k<4;k++) if (i+wx[k]>=1&&i+wx[k]<=n&&j+wy[k]>=1&&j+wy[k]<=m&& (abs(i-X)+abs(j-Y)&1)==a[i][j]&&(abs(i+wx[k]-X)+abs(j+wy[k]-Y)&1)==a[i+wx[k]][j+wy[k]]) { if (!id[i][j]) id[i][j]=++cnt;if (!id[i+wx[k]][j+wy[k]]) id[i+wx[k]][j+wy[k]]=++cnt; addedge(id[i][j],id[i+wx[k]][j+wy[k]]),addedge(id[i+wx[k]][j+wy[k]],id[i][j]); } for (int i=1;i<=cnt;i++) if (!match[i]) memset(flag,0,sizeof(flag)),hungary(i); tag[id[X][Y]]=1; if (match[id[X][Y]]) { memset(flag,0,sizeof(flag)); match[match[id[X][Y]]]=0; win[0]=!hungary(match[id[X][Y]]); } int k=read()<<1; for (int i=1;i<=k;i++) { int x=read(),y=read(); tag[id[x][y]]=1; if (match[id[x][y]]) { memset(flag,0,sizeof(flag)); match[match[id[x][y]]]=0; win[i]=!hungary(match[id[x][y]]); } } int tot=0; for (int i=0;i<k;i+=2) if (win[i]&&win[i+1]) tot++; cout<<tot<<endl; for (int i=0;i<k;i+=2) if (win[i]&&win[i+1]) printf("%d\n",(i>>1)+1); return 0; }