zoukankan      html  css  js  c++  java
  • 【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

  • 相关阅读:
    fastjson对String、JSONObject、JSONArray相互转换
    查看各进程分别占用多少服务器内存
    如何关闭或删除阿里云云盾安骑士
    docker 镜像操作
    docker 容器命令
    docker换源
    centos8 docker安装
    基本概念
    自动生成文件注释和函数注释
    Pycharm新建文件时自动添加基础信息
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/6643796.html
Copyright © 2011-2022 走看看