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;
    }
    
  • 相关阅读:
    【Java】RuleSource约束常用方法整理
    【Oracle】多次提交造成性能慢及处理方法
    【Oracle】ORA-28000: the account is locked-的解决办法
    【Eclipse】几个最重要的快捷键
    JavaScript 垃圾回收总结
    JavaScript 执行环境(作用域)总结
    JavaScript基本数据类型
    全局 Ajax 事件处理器
    require.js 模块化
    配置apache反向代理进行跨域
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793647.html
Copyright © 2011-2022 走看看