首先将棋盘黑白染色,不妨令空格处为黑色。那么移动奇数次后空格一定处于白色格子,偶数次后空格一定处于黑色格子。所以若有某个格子的棋子颜色与棋盘颜色不同,这个棋子就是没有用的。并且空格与某棋子交换后,棋子所在的格子改变使得该棋子与棋盘颜色不同,那么该棋子也会变为无用棋子。那么问题变为空格在棋盘上黑白格子交替移动,棋盘上有障碍物,走过的格子不能再走,不能移动者输。

  显然这是一个二分图博弈。如果所有的最大匹配都包含起点,先手必胜,因为每次只需要沿匹配边走即可,由增广路定理不会出现没边走的情况。否则后手必胜,因为一旦起点不在最大匹配中,走了一条边后后手变成先手,所达点一定在(不包含起点的)最大匹配中。那么每次我们只需要判断空格位置是否处于剩余图的所有最大匹配中,也即判断删除该点后最大匹配是否会减少。这只需要从被删除点原本的匹配点出发找增广路。判断完每次操作后是否处于必胜态后只要看是否从必胜态走到了必胜态即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 42
char getc(){char c=getchar();while (c==||c==||c==) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,k,X,Y,a[N][N],p[N*N],match[N*N],id[N][N],cnt,t,ans;
bool flag[N*N],tag[N*N],win[];
int wx[]={,,,-},wy[]={,-,,};
struct data{int to,nxt;
}edge[N*N<<];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
bool hungary(int k)
{
for (int i=p[k];i;i=edge[i].nxt)
if (!flag[edge[i].to]&&!tag[edge[i].to])
{
int x=edge[i].to;
flag[x]=;
if (!match[x]||!tag[match[x]]&&hungary(match[x]))
{
match[x]=k,match[k]=x;
return ;
}
}
return ;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj2437.in","r",stdin);
freopen("bzoj2437.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
{
char c=getc();
a[i][j]=c=='O';
if (c=='.') X=i,Y=j;
}
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
for (int k=;k<;k++)
if (i+wx[k]>=&&i+wx[k]<=n&&j+wy[k]>=&&j+wy[k]<=m&&
(abs(i-X)+abs(j-Y)&)==a[i][j]&&(abs(i+wx[k]-X)+abs(j+wy[k]-Y)&)==a[i+wx[k]][j+wy[k]])
{
if (!id[i][j]) id[i][j]=++cnt;if (!id[i+wx[k]][j+wy[k]]) id[i+wx[k]][j+wy[k]]=++cnt;
addedge(id[i][j],id[i+wx[k]][j+wy[k]]),addedge(id[i+wx[k]][j+wy[k]],id[i][j]);
}
for (int i=;i<=cnt;i++)
if (!match[i]) memset(flag,,sizeof(flag)),hungary(i);
tag[id[X][Y]]=;
if (match[id[X][Y]])
{
memset(flag,,sizeof(flag));
match[match[id[X][Y]]]=;
win[]=!hungary(match[id[X][Y]]);
}
int k=read()<<;
for (int i=;i<=k;i++)
{
int x=read(),y=read();
tag[id[x][y]]=;
if (match[id[x][y]])
{
memset(flag,,sizeof(flag));
match[match[id[x][y]]]=;
win[i]=!hungary(match[id[x][y]]);
}
}
int tot=;
for (int i=;i<k;i+=) if (win[i]&&win[i+]) tot++;
cout<<tot<<endl;
for (int i=;i<k;i+=) if (win[i]&&win[i+]) printf("%d\n",(i>>)+);
return ;
}

BZOJ2437 NOI2011兔兔与蛋蛋(二分图匹配+博弈)的更多相关文章

  1. 【BZOJ2437】【NOI2011】兔兔与蛋蛋(博弈论,二分图匹配)

    [BZOJ2437][NOI2011]兔兔与蛋蛋(博弈论,二分图匹配) 题面 BZOJ 题解 考虑一下暴力吧. 对于每个状态,无非就是要考虑它是否是必胜状态 这个直接用\(dfs\)爆搜即可. 这样子 ...

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

    未经博主同意不得转载 2437: [Noi2011]兔兔与蛋蛋 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 693  Solved: 442 Des ...

  3. 【bzoj2437】[Noi2011]兔兔与蛋蛋 二分图最大匹配+博弈论

    Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母&quo ...

  4. bzoj 2437[Noi2011]兔兔与蛋蛋 黑白染色二分图+博弈+匈牙利新姿势

    noi2011 兔兔与蛋蛋 题目大意 直接看原题吧 就是\(n*m\)的格子上有一些白棋和一些黑棋和唯一一个空格 兔兔先手,蛋蛋后手 兔兔要把与空格相邻的其中一个白棋移到空格里 蛋蛋要把与空格相邻的其 ...

  5. 博弈论(二分图匹配):NOI 2011 兔兔与蛋蛋游戏

    Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母&quo ...

  6. 2437: [Noi2011]兔兔与蛋蛋 - BZOJ

    Description Input 输入的第一行包含两个正整数 n.m.接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母" ...

  7. NOI2011 兔兔与蛋蛋游戏

    http://www.lydsy.com/JudgeOnline/problem.php?id=2437 这道题真是极好的. 75分做法: 搜索. 出题人真的挺良心的,前15个数据点的范围都很小,可以 ...

  8. 【LOJ】#2447. 「NOI2011」兔兔与蛋蛋的游戏

    题解 对于75分来说,操作肯定不会成环,可以暴搜 看成空格在移动,空格移动到原来的位置肯定经历了偶数个格子,但是操作的人是两个不同的人,所以肯定不会成环 对于满分做法,要找到一种更好的方式判先手是否会 ...

  9. bzoj 2437 [Noi2011]兔子和鸡蛋 [二分图匹配]

    叙述性说明 这些日子.兔子和蛋像一个新的棋盘游戏. 这场比赛是在 n 行 m 在船上进行列. 前,棋盘上有一 个格子是空的,其他的格子中都放置了一枚棋子,棋子或者是黑色,或者是白色. 每一局游戏总是兔 ...

随机推荐

  1. CacheCloud+Redis Cluster 3部署

    CacheCloud环境需求 Java 7 Maven 3 MySQL Redis 3 具体用法可参考:https://cachecloud.github.io 1.下载CacheCloud 官网ht ...

  2. 关于antd-mobile中列表上拉加载PullToRefresh的使用

    相信有很多小伙伴发现antd-mobile中的下拉刷新组件,也发现例子挺难的,其实这个组件并没有那么复杂,只是demo例子不好理解,给大家提供一个简单的demo,或许可以帮到你 上拉刷新下拉加载 - ...

  3. c#winform使用WebBrowser 大全

    C# WinForm WebBrowser (一) MSDN资料 1.主要用途:使用户可以在窗体中导航网页. 2.注意:WebBrowser 控件会占用大量资源.使用完该控件后一定要调用 Dispos ...

  4. Leecode刷题之旅-C语言/python-58.最后一个单词的长度

    /* * @lc app=leetcode.cn id=58 lang=c * * [58] 最后一个单词的长度 * * https://leetcode-cn.com/problems/length ...

  5. rhel6.4 根目录扩容

    状况:根目录容量不足 解决:扩容根目录 ====================================================== 解决步骤: 1. 将新的磁盘加入服务器 2. 使用 ...

  6. LeetCode算法1—— 两数之和

    给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], target ...

  7. 【转】mui 通过JSON动态的生成列表

    <script type="text/template" id="radio-tigan"> <%for(var i=0;i<recor ...

  8. 通过transpose和flip实现图像旋转90/180/270度

    在fbc_cv库中,提供了对图像进行任意角度旋转的函数rotate,其实内部也是调用了仿射变换函数warpAffine.如果图像仅是进行90度倍数的旋转,是没有必要用warpAffine函数的.这里通 ...

  9. (原)一段看似美丽的for循环,背后又隐藏着什么

    之前很长一段时间,潜心修炼汇编,专门装了一个dos7,慢慢玩到win32汇编,再到linux的AT&A汇编,尝试写mbr的时候期间好几次把centos弄的开不了机,又莫名其妙的修好了,如今最大 ...

  10. vue循环绑定v-model

    直接上代码 结构: <repayInput v-if="formData" v-for="(item, index) in formData" :isPw ...