【bzoj2437】【NOI2011】【兔兔与蛋蛋】【二分图博弈】

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

题解:考虑操作路径一定是把空格交替移动到O和X.

           如果对图进行黑白染色的话,操作路径也一定是黑白交替。

           我们设定空格是X,颜色是黑色。

           那么只有黑色的X和白色的O是有用的点。

           我们把相邻的这些点连边。然后就是标准的二分图博弈了。

           每次只需要判断删去当前点是否还能得到最大匹配,对当前点的匹配点单独find一下即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 110
using namespace std;
int point[N*N],next[N*N],pos[N][N],n,m,a[N][N],tot;
int vis[N*N],bx,by,num,bl[N*N],q,p[N*N],ans,f[N*N],cnt,t;
struct use{int st,en;}e[N*N];
char ch[N][N];
int Abs(int x){if (x<0) return -x;else return x;}
void add(int x,int y){next[++cnt]=point[x];point[x]=cnt;e[cnt].en=y;} 
bool find(int x){
  for (int i=point[x];i;i=next[i]){
  	  if (vis[e[i].en]==tot) continue;
     if (f[e[i].en]==1) continue;
     vis[e[i].en]=tot;
     if (!bl[e[i].en]||find(bl[e[i].en])){
       bl[e[i].en]=x;bl[x]=e[i].en;
       return true;
     }
 }
   return false;
}
int main(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) scanf("%s",ch[i]+1);
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
    if (ch[i][j]=='.') bx=i,by=j;
  ch[bx][by]='X';
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
    if (ch[i][j]=='O'&&(Abs(i-bx)+Abs(j-by))%2==1||ch[i][j]=='X'&&(Abs(i-bx)+Abs(j-by))%2==0)
  pos[i][j]=++num;
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
    if (pos[i][j]){
      if (pos[i-1][j]) add(pos[i][j],pos[i-1][j]);
      if (pos[i+1][j]) add(pos[i][j],pos[i+1][j]);
      if (pos[i][j-1]) add(pos[i][j],pos[i][j-1]);
      if (pos[i][j+1]) add(pos[i][j],pos[i][j+1]);
    }
  for (int i=1;i<=num;i++)if(!bl[i]){tot++;if (find(i)) t++;}
  scanf("%d",&q);q<<=1;
  for (int i=1;i<=q;i++){
  	if (bl[pos[bx][by]]){
  	   int t=bl[pos[bx][by]];
  	   bl[pos[bx][by]]=bl[t]=0;
  	   f[pos[bx][by]]=1;
  	   tot++;p[i]=find(t);
  	}
  	else p[i]=1,f[pos[bx][by]]=1;
    scanf("%d%d",&bx,&by);
  }
  for (int i=1;i<=q;i+=2) if (!p[i]&&!p[i+1]) ans++;
  printf("%d\n",ans);
  for (int i=1;i<=q;i+=2) if (!p[i]&&!p[i+1]) printf("%d\n",(i+1)>>1);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值