【BZOJ 2437】 2437: [Noi2011]兔兔与蛋蛋 (博弈+二分图匹配**)

未经博主同意不得转载

 

2437: [Noi2011]兔兔与蛋蛋

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 693  Solved: 442

Description

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≤ 40

Sample 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 3

Sample 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
",op[0]); 
 86     for(int i=1;i<=op[0];i++) printf("%d
",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 }
View Code

2017-03-30 12:52:17

原文地址:https://www.cnblogs.com/Konjakmoyu/p/6643796.html