zoukankan      html  css  js  c++  java
  • 【NOI2011T6】兔兔与蛋蛋的游戏-二分图最大匹配+博弈

    测试地址:兔兔与蛋蛋的游戏
    做法:这题真是好题…虽然放到考场上不一定会做…
    这一道题需要用到二分图最大匹配+博弈(又是一道披着博弈皮的二分图题)。
    要完成这一题,我们必须证出两个重要的结论。我们把“将棋子移入空格”看做“将空格移到棋子的位置”,然后我们就开始证明。
    结论1:空格所走过的路径不会经过同一个点两次。
    证明:反证法,如果路径经过同一个点两次,就说明路径上有环,由于是在矩形棋盘上走,显然环必然是偶数条边组成的,不妨设空格第一次到达该点时,下一步换入的棋子为白色,那么奇数步换入白色的棋子,偶数步应该换入黑色的棋子,而第二次到达该点的前一步是偶数步,而前面换到这一点的棋子是白色,矛盾,因而结论得证。
    证明了这一结论之后,我们就可以找出所有可以走到的点,显然,空格周围的白色棋子可以走到,而这些白色棋子周围的黑色棋子也可以走到……以此类推,可以用一次BFS找出所有的可行点。我的代码里是用奇偶性判断的,这个看看就行,还是BFS比较简单吧。这样一来,问题就转化为:从一个点开始走,双方轮流行动,每次移动一格,同一个点不能经过两次,不能走的就失败,问先手必胜还是必败。
    我们把可行点找出来之后,再对棋盘黑白染色,棋盘就可以转化为一个二分图,这时候我们就证明第二个结论:
    结论2:如果一个点一定被二分图的最大匹配覆盖,那么从这个起点开始走先手必胜,否则先手必败。
    证明:在这种情况下,先手每次走匹配边,而后手只能走非匹配边,那么后手必败。而如果起点为非匹配点,若先手某一步走到了一个非匹配点,那么从起点到这一点的路径就是增广路,和最大匹配的前提矛盾,那么先手一定会走到一个匹配点,所以后手只需走匹配边即可,这样的话先手就必败了。综上所述,结论得证。
    那么我们就可以判断从某个点开始是不是先手必胜的了,步骤如下:从这个点开始BFS,从这一侧走到另一侧只走匹配边,而从另一侧走回这一侧只走非匹配边,如果走到这一侧的某个点时,这个点不在匹配中,那么就判断原来那个点先手必败。如果直到退出都没找到这样一个点,则先手必胜。算法正确性证明如下:按照这种方法找到一个点,那么BFS时所经过的从起点到该点的路径是匹配边-非匹配边交错的,且匹配边和非匹配边数量相同,交换匹配边和非匹配边,即可得到不包含起点的一个最大匹配。
    那么我们只需按照操作顺序,检查从某个点出发是否先手必胜即可,因为过程中有些点不能再走了,所以就把这些点删掉(也就是打个标记)再做。但是我们没有必要每次都做一次二分图最大匹配,因为每次最多删掉一个点,我们只需对删掉的点的匹配点再找一条增广路即可。设点数为N,总复杂度应该是O(N2+kN),可以通过这道题(本人的总时间是20ms,非常优秀了)。
    犯二的地方:搜索的时候忘记不能走已经删掉的点了,而且每次找增广路时要清空一个标记数组,以后要注意。
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,k,p,sx,sy,ans=0,anst[1010]={0},q[1610],h,t;
    int tot=0,first[1610]={0},mat[1610];
    struct edge {int v,next;} e[7010];
    char s[50][50];
    bool forb[1610]={0},vis[1610]={0};
    
    void insert(int a,int b)
    {
      e[++tot].v=b,e[tot].next=first[a],first[a]=tot;
      e[++tot].v=a,e[tot].next=first[b],first[b]=tot;
    }
    
    void init()
    {
      scanf("%d%d",&n,&m);
      for(int i=0;i<n;i++)
        scanf("%s",s[i]);
    
      for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
          if (s[i][j]=='.')
          {
            sx=i,sy=j;
            p=(sx+sy)%2;
            break;
          }
      for(int i=0;i<n;i++)
      {
        for(int j=0;j<m;j++)
        {
          if (s[i][j]=='.') s[i][j]='X';
          if (s[i][j]=='O'&&(i+j)%2==p) continue;
          if (s[i][j]=='X'&&(i+j)%2!=p) continue;
          if (i>0&&s[i-1][j]=='O'&&(i+j-1)%2!=p)
            insert((i-1)*m+j,i*m+j);
          if (i>0&&s[i-1][j]=='X'&&(i+j-1)%2==p)
            insert((i-1)*m+j,i*m+j);
          if (j>0&&s[i][j-1]=='O'&&(i+j-1)%2!=p)
            insert(i*m+j-1,i*m+j);
          if (j>0&&s[i][j-1]=='X'&&(i+j-1)%2==p)
            insert(i*m+j-1,i*m+j);
        }
      }
    }
    
    bool find_match(int v)
    {
      if (vis[v]||forb[v]) return 0;
      vis[v]=1;
      for(int i=first[v];i;i=e[i].next)
      {
        if (vis[e[i].v]||forb[e[i].v]) continue;
        if (mat[e[i].v]==-1||find_match(mat[e[i].v]))
        {
          mat[e[i].v]=v;
          mat[v]=e[i].v;
          return 1;
        }
      }
      return 0;
    }
    
    void hungary()
    {
      memset(mat,-1,sizeof(mat));
      for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
          if (s[i][j]=='O'&&(i+j)%2!=p&&mat[i*m+j]==-1)
          {
            memset(vis,0,sizeof(vis));
            find_match(i*m+j);
          }
    }
    
    bool check(int v)
    {
      memset(vis,0,sizeof(vis));
      h=1,t=1;
      q[1]=v;
      vis[v]=1;
      while(h<=t)
      {
        int x=q[h];
        if (mat[x]==-1) return 0;
        for(int i=first[mat[x]];i;i=e[i].next)
          if (!vis[e[i].v]&&!forb[e[i].v])
          {
            vis[e[i].v]=1;
            q[++t]=e[i].v;
          }
        h++;
      }
      return 1;
    }
    
    void forbid(int v)
    {
      forb[v]=1;
      if (mat[v]!=-1)
      {
        mat[mat[v]]=-1;
        memset(vis,0,sizeof(vis));
        find_match(mat[v]);
        mat[v]=-1;
      }
    }
    
    void work()
    {
      hungary();
      scanf("%d",&k);
      for(int i=1;i<=k;i++)
      {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        x1--,y1--,x2--,y2--;
        bool f=check(sx*m+sy);
        forbid(sx*m+sy);
        if (f&&check(x1*m+y1)) anst[++ans]=i;
        forbid(x1*m+y1);
        sx=x2,sy=y2;
      }
    
      printf("%d
    ",ans);
      for(int i=1;i<=ans;i++)
        printf("%d
    ",anst[i]);
    }
    
    int main()
    {
      init();
      work();
    
      return 0;
    }
  • 相关阅读:
    谁知道怎么获得客户端的语系,不是encoding哦
    如何动态控制弹出窗体的大小
    维权成功!
    KFC的mm在练习做圣代
    CICD自动化发版系统设计简介
    java IO (File类)
    StringBuffer
    Arrays和比较器
    java正则
    Math和Random类
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793646.html
Copyright © 2011-2022 走看看