【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\n");
  for(int i=0;i<n*m;i++)
    if (ans[i]) printf("%d %d\n",i/m+1,i%m+1);

  return 0;
}
posted @ 2017-06-08 12:24  Maxwei_wzj  阅读(112)  评论(0编辑  收藏  举报