【BZOJ 2437】 2437: [Noi2011]兔兔与蛋蛋 (博弈+二分图匹配**)

2022-11-24,,,,

未经博主同意不得转载

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

Day2

【分析】

  神题。。

  首先。。博弈部分,用二分图匹配来做。

  题目可以看成是空白格在移动。因为是白黑白黑地走,显然呢是有一些点是无用的。比如距离起始点为偶数格的白点。。

  然后剩下的点分两块,黑点和白点,弄成二分图,相邻的黑白点连边,一开始的空白格可以看成是黑点。

  想想白怎么样能赢呢?就是走到一个白点,然后你有方式无论黑怎么走你都能使结束点在白点。

  然后就有一个结论:

  如果当前所在的点是必须点,那么必胜。否则必败。

  必须点指的是这个点一定在所有可能的最大二分图匹配中。非必需点是它的补集。

  为什么呢?【下面只说你怎么赢和你为什么不会输】

  首先说必须点必胜。

  

  从必须点出发,你的策略是每次都走匹配边(选择任意一个最大匹配就好),那么别人就一定走的是非匹配边嘛。

  一定是在匹配边结束(结束指找不到别的没走过的边走了)。否则,如右图,那么这条路的匹配边和非匹配边互换,又是一个最大二分匹配,那么一开始的必须点不用选择,与其必须点的身份矛盾,是不可能的。

  然后是非必须点必败。

   

  

  这时后手掌握必胜态。你从非必须点出发,一定走到了一条在某最大匹配方案中的非匹配边(因为是非必须点),那么别人就根据这个最大匹配的方案每次走匹配边。

  一定是在匹配边结束。否则,如右图,那么这条路的匹配边和非匹配边互换,是可以交替的增广路,与其最大匹配的身份矛盾,是不可能的。

  所以判断胜负只要判断当前所在点是否必须。

  打法见代码【膜大颓果漂亮打法】,比我之前想的好多了。判断是否必须点,只要删掉这个点,看看还能不能增广就好了。

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define Maxn 50
#define Maxq 2010 int n,m; struct node
{
int x,y,next;
}t[Maxn*Maxn*];
int first[Maxn*Maxn],len; void ins(int x,int y)
{
t[++len].x=x;t[len].y=y;
t[len].next=first[x];first[x]=len;
} int a[Maxn][Maxn],num[Maxn][Maxn];
int tot; int match[Maxn*Maxn],chw[Maxn*Maxn];
bool vis[Maxn*Maxn]; bool ffind(int x,int nt)
{
if(!vis[x]) return ;
for(int i=first[x];i;i=t[i].next) if(chw[t[i].y]!=nt&&vis[t[i].y])
{
int y=t[i].y;
chw[y]=nt;
if(!match[y]||ffind(match[y],nt))
{
match[y]=x;match[x]=y;
return ;
}
}
return ;
} int nt,op[Maxn*Maxn];
int bx[]={,,,-,},
by[]={,,,,-};
char s[Maxn]; bool ret[Maxn*Maxn]; int nx,ny;
void get_ans()
{
memset(match,,sizeof(match));
memset(chw,,sizeof(chw));
nt=;
for(int i=;i<=tot;i++) vis[i]=;
for(int i=;i<=tot;i++) if(!match[i])
{
nt++;
ffind(i,nt);
}
int q;
scanf("%d",&q);q<<=;
op[]=;
for(int i=;i<=q;i++)
{
vis[num[nx][ny]]=;
if(match[num[nx][ny]])
{
int nw=match[num[nx][ny]];
match[nw]=match[num[nx][ny]]=;
ret[i]=!ffind(nw,++nt);//找不到 必须点 必胜
}
else
{
ret[i]=;//不在最优匹配中 不是必须点 必败
}
scanf("%d%d",&nx,&ny);
}
for(int i=;i<=q;i+=)
if(ret[i]&&ret[i+]) op[++op[]]=(i+)/;
printf("%d\n",op[]);
for(int i=;i<=op[];i++) printf("%d\n",op[i]);
} int main()
{
scanf("%d%d",&n,&m);
len=;tot=;
for(int i=;i<=n;i++)
{
scanf("%s",s+);
for(int j=;j<=m;j++)
{
if(s[j]=='X') a[i][j]=;
else if(s[j]=='O') a[i][j]=;
else a[i][j+]=,nx=i,ny=j;
num[i][j]=++tot;
}
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
for(int k=;k<=;k++)
{
int xx=i+bx[k],yy=j+by[k];
if(xx<||yy<||xx>n||yy>m) continue;
if(a[xx][yy]==a[i][j]) continue;
ins(num[i][j],num[xx][yy]);
}
}
get_ans();
return ;
}

2017-03-30 12:52:17

BZOJ 2437】 2437: [Noi2011]兔兔与蛋蛋 (博弈+二分图匹配**)的相关教程结束。

《【BZOJ 2437】 2437: [Noi2011]兔兔与蛋蛋 (博弈+二分图匹配**).doc》

下载本文的Word格式文档,以方便收藏与打印。