zoukankan      html  css  js  c++  java
  • 【BZOJ1443】游戏Game(JSOI2009)-二分图最大匹配+博弈

    测试地址:游戏Game
    做法:这一题的思想十分巧妙……这是一道披着博弈皮的二分图匹配题啊……
    这一题需要用到二分图最大匹配+博弈。
    首先我们注意到,如果将矩形黑白间隔染色,那么矩形上任何一条边都连接黑白两色的点,这启发我们把矩阵转化成二分图进行处理。然后我们研究什么样的起点才能保证后手必胜(这里称先走的为先手)。
    显然,如果二分图存在完备匹配,那么后手必败,因为先手只需要按着匹配边走,就可以把后手卡死,那么反之,如果二分图不存在完备匹配,那么一定存在一个起点使得后手必胜。那么这样的点有什么性质呢?先说结论:如果存在一个最大匹配不包含某点,那么这个点就是使得后手必胜的点。证明如下:先手从左侧某点走出时,可能走到匹配点或非匹配点,如果走到匹配点,那么后手就走匹配边即可,如果走到非匹配点,那么起点走到这个点的路径就是一条增广路,这显然与最大匹配的前提矛盾,那么这样一直走下去,先手一定会被卡死,综上所述,以这样的点作为起点则后手必胜。那么现在问题就变成了怎么找这样的点。
    有的同学可能会想,我们做一次最大匹配,把没有覆盖到的点直接输出不就好了吗?注意我上面说的,我们要找的点是“存在一个最大匹配不包含的”,而最大匹配可能有很多,那么要怎么办呢?我们可以做两次最大匹配,每次只考虑二分图的一侧上有没有可能的起点,那么我们可以这样做:首先做一次最大匹配,然后把没被覆盖到的点放进队列,然后每个点可以扩展到另一侧的匹配点,那么这些匹配点所对应的一侧上的点就也是答案,再把这些点放进队列。为什么这样做是对的呢?注意到我们如果按照以上走法,走过的路径应该是一条类似增广路的路径,只不过非匹配边和匹配边数量相等,那么我们把非匹配边换成匹配边,把匹配边换成非匹配边,匹配数是不变的,这样变换之后还是一个最大匹配,但是这样的话走到的点就会变成非匹配点,那么和条件“存在一个最大匹配不包含”相符,所以这个点也是答案。这样我们就解决了这一问题。
    做二分图最大匹配时,匈牙利算法的时间复杂度是平方级的,虽然看上去有10000个点跑不过,但是匈牙利算法常数很小,而且搭配这个图的特殊性,常数就更小了,所以时间是不用担心的,亲测可过。
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,tot=0,first[10010]={0},q[10010]={0},vis[10010]={0},h,t;
    int l[5010]={0},r[5010]={0},mat[10010],maxmat;
    char s[110][110];
    bool ans[10010]={0};
    struct edge {int v,next;} e[50010];
    
    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;
    }
    
    bool find_match(int v,int p)
    {
      vis[v]=p;
      for(int i=first[v];i;i=e[i].next)
      {
        if (mat[e[i].v]==-1)
        {
          mat[e[i].v]=v;
          mat[v]=e[i].v;
          return 1;
        }
        else if (vis[mat[e[i].v]]!=p&&find_match(mat[e[i].v],p))
        {
          mat[e[i].v]=v;
          mat[v]=e[i].v;
          return 1;
        }
      }
      return 0;
    }
    
    void find_answer()
    {
      for(int i=h;i<=t;i++) vis[q[i]]=1;
      while(h<=t)
      {
        int v=q[h];
        ans[v]=1;
        for(int i=first[v];i;i=e[i].next)
          if (mat[e[i].v]!=-1&&!vis[mat[e[i].v]])
          {
            vis[mat[e[i].v]]=1;
            q[++t]=mat[e[i].v];
          }
        h++;
      }
    }
    
    void hungary(bool mode)
    {
      maxmat=0;
      memset(mat,-1,sizeof(mat));
      memset(vis,0,sizeof(vis));
      h=1,t=0;
      if (!mode)
      {
        for(int i=1;i<=l[0];i++)
          if (mat[l[i]]==-1)
            if (find_match(l[i],i)) maxmat++;
        for(int i=1;i<=l[0];i++)
          if (mat[l[i]]==-1) q[++t]=l[i];
      }
      else
      {
        for(int i=1;i<=r[0];i++)
          if (mat[r[i]]==-1)
            if (find_match(r[i],i)) maxmat++;
        for(int i=1;i<=r[0];i++)
          if (mat[r[i]]==-1) q[++t]=r[i];
      }
      memset(vis,0,sizeof(vis));
      find_answer();
    }
    
    int main()
    {
      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]=='#') continue;
          if ((i+j)%2==0) l[++l[0]]=i*m+j;
          else r[++r[0]]=i*m+j;
          if (j>0&&s[i][j-1]=='.') insert(i*m+j-1,i*m+j);
          if (i>0&&s[i-1][j]=='.') insert((i-1)*m+j,i*m+j);
        }
    
      hungary(0);
      if (maxmat==l[0]&&maxmat==r[0]) {printf("LOSE");return 0;}
      hungary(1);
    
      printf("WIN
    ");
      for(int i=0;i<n*m;i++)
        if (ans[i]) printf("%d %d
    ",i/m+1,i%m+1);
    
      return 0;
    }
    
  • 相关阅读:
    php多态
    ssl certificate problem: self signed certificate in certificate chain
    test plugin
    open specific port on ubuntu
    junit vs testng
    jersey rest service
    toast master
    use curl to test java webservice
    update folder access
    elk
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793647.html
Copyright © 2011-2022 走看看