【BZOJ 2437】 2437: [Noi2011]兔兔与蛋蛋 (博弈+二分图匹配**)
未经博主同意不得转载
2437: [Noi2011]兔兔与蛋蛋
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 693 Solved: 442Description
Input
输入的第一行包含两个正整数 n、m。
接下来 n行描述初始棋盘。其中第i 行包含 m个字符,每个字符都是大写英文字母"X"、大写英文字母"O"或点号"."之一,分别表示对应的棋盘格中有黑色棋子、有白色棋子和没有棋子。其中点号"."恰好出现一次。
接下来一行包含一个整数 k(1≤k≤1000) ,表示兔兔和蛋蛋各进行了k次操作。
接下来 2k行描述一局游戏的过程。其中第 2i – 1行是兔兔的第 i 次操作(编号为i的操作) ,第2i行是蛋蛋的第i次操作。每个操作使用两个整数x,y来描述,表示将第x行第y列中的棋子移进空格中。
输入保证整个棋盘中只有一个格子没有棋子, 游戏过程中兔兔和蛋蛋的每个操作都是合法的,且最后蛋蛋获胜。Output
输出文件的第一行包含一个整数r,表示兔兔犯错误的总次数。
接下来r 行按递增的顺序给出兔兔“犯错误”的操作编号。其中第 i 行包含一个整数ai表示兔兔第i 个犯错误的操作是他在游戏中的第 ai次操作。
1 ≤n≤ 40, 1 ≤m≤ 40Sample Input
样例一:
1 6
XO.OXO
1
1 2
1 1
样例二:
3 3
XOX
O.O
XOX
4
2 3
1 3
1 2
1 1
2 1
3 1
3 2
3 3
样例三:
4 4
OOXX
OXXO
OO.O
XXXO
2
3 2
2 2
1 2
1 3Sample Output
样例一:
1
1
样例二:
0
样例三:
2
1
2
样例1对应图一中的游戏过程
样例2对应图三中的游戏过程HINT
Source
【分析】
神题。。
首先。。博弈部分,用二分图匹配来做。
题目可以看成是空白格在移动。因为是白黑白黑地走,显然呢是有一些点是无用的。比如距离起始点为偶数格的白点。。
然后剩下的点分两块,黑点和白点,弄成二分图,相邻的黑白点连边,一开始的空白格可以看成是黑点。
想想白怎么样能赢呢?就是走到一个白点,然后你有方式无论黑怎么走你都能使结束点在白点。
然后就有一个结论:
如果当前所在的点是必须点,那么必胜。否则必败。
必须点指的是这个点一定在所有可能的最大二分图匹配中。非必需点是它的补集。
为什么呢?【下面只说你怎么赢和你为什么不会输】
首先说必须点必胜。
从必须点出发,你的策略是每次都走匹配边(选择任意一个最大匹配就好),那么别人就一定走的是非匹配边嘛。
一定是在匹配边结束(结束指找不到别的没走过的边走了)。否则,如右图,那么这条路的匹配边和非匹配边互换,又是一个最大二分匹配,那么一开始的必须点不用选择,与其必须点的身份矛盾,是不可能的。
然后是非必须点必败。
这时后手掌握必胜态。你从非必须点出发,一定走到了一条在某最大匹配方案中的非匹配边(因为是非必须点),那么别人就根据这个最大匹配的方案每次走匹配边。
一定是在匹配边结束。否则,如右图,那么这条路的匹配边和非匹配边互换,是可以交替的增广路,与其最大匹配的身份矛盾,是不可能的。
所以判断胜负只要判断当前所在点是否必须。
打法见代码【膜大颓果漂亮打法】,比我之前想的好多了。判断是否必须点,只要删掉这个点,看看还能不能增广就好了。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Maxn 50 8 #define Maxq 2010 9 10 int n,m; 11 12 struct node 13 { 14 int x,y,next; 15 }t[Maxn*Maxn*4]; 16 int first[Maxn*Maxn],len; 17 18 void ins(int x,int y) 19 { 20 t[++len].x=x;t[len].y=y; 21 t[len].next=first[x];first[x]=len; 22 } 23 24 int a[Maxn][Maxn],num[Maxn][Maxn]; 25 int tot; 26 27 int match[Maxn*Maxn],chw[Maxn*Maxn]; 28 bool vis[Maxn*Maxn]; 29 30 bool ffind(int x,int nt) 31 { 32 if(!vis[x]) return 0; 33 for(int i=first[x];i;i=t[i].next) if(chw[t[i].y]!=nt&&vis[t[i].y]) 34 { 35 int y=t[i].y; 36 chw[y]=nt; 37 if(!match[y]||ffind(match[y],nt)) 38 { 39 match[y]=x;match[x]=y; 40 return 1; 41 } 42 } 43 return 0; 44 } 45 46 int nt,op[Maxn*Maxn]; 47 int bx[6]={0,1,0,-1,0}, 48 by[6]={0,0,1,0,-1}; 49 char s[Maxn]; 50 51 bool ret[Maxn*Maxn]; 52 53 int nx,ny; 54 void get_ans() 55 { 56 memset(match,0,sizeof(match)); 57 memset(chw,0,sizeof(chw)); 58 nt=0; 59 for(int i=1;i<=tot;i++) vis[i]=1; 60 for(int i=1;i<=tot;i++) if(!match[i]) 61 { 62 nt++; 63 ffind(i,nt); 64 } 65 int q; 66 scanf("%d",&q);q<<=1; 67 op[0]=0; 68 for(int i=1;i<=q;i++) 69 { 70 vis[num[nx][ny]]=0; 71 if(match[num[nx][ny]]) 72 { 73 int nw=match[num[nx][ny]]; 74 match[nw]=match[num[nx][ny]]=0; 75 ret[i]=!ffind(nw,++nt);//找不到 必须点 必胜 76 } 77 else 78 { 79 ret[i]=0;//不在最优匹配中 不是必须点 必败 80 } 81 scanf("%d%d",&nx,&ny); 82 } 83 for(int i=1;i<=q;i+=2) 84 if(ret[i]&&ret[i+1]) op[++op[0]]=(i+1)/2; 85 printf("%d\n",op[0]); 86 for(int i=1;i<=op[0];i++) printf("%d\n",op[i]); 87 } 88 89 int main() 90 { 91 scanf("%d%d",&n,&m); 92 len=0;tot=0; 93 for(int i=1;i<=n;i++) 94 { 95 scanf("%s",s+1); 96 for(int j=1;j<=m;j++) 97 { 98 if(s[j]=='X') a[i][j]=0; 99 else if(s[j]=='O') a[i][j]=1; 100 else a[i][j+1]=0,nx=i,ny=j; 101 num[i][j]=++tot; 102 } 103 } 104 for(int i=1;i<=n;i++) 105 for(int j=1;j<=m;j++) 106 { 107 for(int k=1;k<=4;k++) 108 { 109 int xx=i+bx[k],yy=j+by[k]; 110 if(xx<1||yy<1||xx>n||yy>m) continue; 111 if(a[xx][yy]==a[i][j]) continue; 112 ins(num[i][j],num[xx][yy]); 113 } 114 } 115 get_ans(); 116 return 0; 117 }
2017-03-30 12:52:17