bzoj2437-兔兔与蛋蛋
题目

分析
第一次做这种题,其实很简单。
只能经过一次的博弈可以考虑转化为二分图博弈。
棋盘上有黑白色的棋子,可以把这个游戏看作空格在棋子间移动,于是就想到,把棋盘黑白染色,以空格为黑,那么空格的移动轨迹一定是黑白相间的。发现有一些棋子空格是移不过去的,那就是染色与棋子颜色不同的点(由于兔兔白棋先走,所以把空格染成黑色,可以符合要求)。剩下的点,把它们分成两部分,黑色和白色,那么可以组成一个二分图,所有的路径其实就是二分图上的路径,因为不可能有两个白色或黑色的棋子相邻。这个问题转化成了一个二分图上的博弈问题,不能走到重复的点。
对于二分图上的博弈,有一个结论:必胜点必定在最大匹配上。也就是说,如果有多种匹配情况,而一个点在某些情况中在最大匹配中,而在其他情况中不在,那么这个点不是必胜的。
下面说明这个结论:
如果一个点\(x\)一定在最大匹配中,那么当前操作者沿着这个点的匹配边走出去,走到对面的匹配点\(y\)。点\(x\)已经不能再走了,但由于\(x\)必定在最大匹配中,这就说明,删除点\(x\)后点\(y\)找不到一条新的增广路,所以\(y\)能走到的任意下一条边一定是非匹配边,连接着一个匹配点。所以轮到先手时,总是可以在一个匹配点上,它的匹配边连出去的另一个点没有被走过。由于边不可能有无限多,而先手总是有办法走,所以后手一定会输。
所以这题直接记录每一个走到的点是否一定在新图的最大匹配中,如果兔兔走之前在,之后也在,那么她就走错了。注意要把走过的点删掉。
代码
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int xx[]={-1,0,1,0};
const int yy[]={0,1,0,-1};
const int maxn=51;
const int maxm=maxn*maxn;
char s[maxn][maxn];
bool a[maxn][maxn],alr[maxm],cannot[maxm],able[maxm];
int white[maxm];
int sx,sy,n,m,wrong[maxm],wt=0,match[maxm],all;
struct egde {
int v,nxt;
} e[maxm<<3];
int h[maxm],tot=0;
void add(int u,int v) {
e[++tot]=(egde){v,h[u]};
h[u]=tot;
}
int dis(int ax,int ay,int bx,int by) {
return abs(ax-bx)+abs(ay-by);
}
int ID(int x,int y) {
return (x-1)*m+y;
}
int change(int x) {
return x&1?x+1:x-1;
}
bool dfs(int x) {
for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (!alr[v] && able[v]) {
alr[v]=true;
if (!match[v] || dfs(match[v])) {
match[v]=x;
match[x]=v;
return true;
}
}
return false;
}
bool win[maxm];
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
n=read(),m=read();
all=ID(n,m);
for (int i=1;i<=n;++i) {
scanf("%s",s[i]+1);
for (int j=1;j<=m;++j) if (s[i][j]=='.') sx=i,sy=j;
}
for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) {
int id=ID(i,j);
white[id]=dis(i,j,sx,sy)&1;
if ((s[i][j]=='O' && white[id]) || ((s[i][j]=='X' || s[i][j]=='.') && !white[id])) able[id]=true; else able[id]=false;
}
for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) {
int aid=ID(i,j);
if (!able[aid]) continue;
for (int k=0;k<4;++k) {
int x=xx[k]+i,y=yy[k]+j;
if (x<1 || x>n || y<1 || y>m) continue;
int bid=ID(x,y);
if (able[bid]) {
add(bid,aid),add(aid,bid);
}
}
}
for (int i=1;i<=all;++i) if (!white[i]) {
memset(alr,0,sizeof alr);
dfs(i);
}
int k=read();
for (int i=1;i<=(k<<1);++i) {
int id=ID(sx,sy);
able[id]=false;
if (match[id]) {
int tmp=match[id];
match[id]=match[tmp]=0;
memset(alr,0,sizeof alr);
win[i]=!dfs(tmp);
} else win[i]=false;
sx=read(),sy=read();
}
wt=0;
for (int i=1;i<=k;++i) if (win[i*2-1] && win[i*2]) ++wt;
printf("%d\n",wt);
for (int i=1;i<=k;++i) if (win[i*2-1] && win[i*2]) printf("%d\n",i);
return 0;
}
bzoj2437-兔兔与蛋蛋的更多相关文章
- 【BZOJ2437】【NOI2011】兔兔与蛋蛋(博弈论,二分图匹配)
[BZOJ2437][NOI2011]兔兔与蛋蛋(博弈论,二分图匹配) 题面 BZOJ 题解 考虑一下暴力吧. 对于每个状态,无非就是要考虑它是否是必胜状态 这个直接用\(dfs\)爆搜即可. 这样子 ...
- 【bzoj2437】[Noi2011]兔兔与蛋蛋 二分图最大匹配+博弈论
Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母&quo ...
- 2437: [Noi2011]兔兔与蛋蛋 - BZOJ
Description Input 输入的第一行包含两个正整数 n.m.接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母" ...
- 博弈论(二分图匹配):NOI 2011 兔兔与蛋蛋游戏
Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母&quo ...
- NOI2011 兔兔与蛋蛋游戏
http://www.lydsy.com/JudgeOnline/problem.php?id=2437 这道题真是极好的. 75分做法: 搜索. 出题人真的挺良心的,前15个数据点的范围都很小,可以 ...
- 【BZOJ 2437】 2437: [Noi2011]兔兔与蛋蛋 (博弈+二分图匹配**)
未经博主同意不得转载 2437: [Noi2011]兔兔与蛋蛋 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 693 Solved: 442 Des ...
- bzoj 2437[Noi2011]兔兔与蛋蛋 黑白染色二分图+博弈+匈牙利新姿势
noi2011 兔兔与蛋蛋 题目大意 直接看原题吧 就是\(n*m\)的格子上有一些白棋和一些黑棋和唯一一个空格 兔兔先手,蛋蛋后手 兔兔要把与空格相邻的其中一个白棋移到空格里 蛋蛋要把与空格相邻的其 ...
- BZOJ2437 [Noi2011]兔兔与蛋蛋 【博弈论 + 二分图匹配】
题目链接 BZOJ2437 题解 和JSOI2014很像 只不过这题动态删点 如果我们把空位置看做\(X\)的话,就会发现我们走的路径是一个\(OX\)交错的路径 然后将图二分染色,当前点必胜,当且仅 ...
- BZOJ2437 NOI2011兔兔与蛋蛋(二分图匹配+博弈)
首先将棋盘黑白染色,不妨令空格处为黑色.那么移动奇数次后空格一定处于白色格子,偶数次后空格一定处于黑色格子.所以若有某个格子的棋子颜色与棋盘颜色不同,这个棋子就是没有用的.并且空格与某棋子交换后,棋子 ...
- bzoj2437 [Noi2011]兔兔与蛋蛋
二分图博弈果然都是一个套路,必经点必胜,非必经点必败, 但是肯定不能每走一步就重新建图判断必胜还是必败,那么我们可以这样:每走一步就把这个点删掉,然后find他原来的匹配,如果找不到,就说明他是必经点 ...
随机推荐
- PHP.52-TP框架商城应用实例-前台4-商品详情页-面包屑导航、AJAX浏览历史
面包屑导航 思路:根据商品的主分类向上取出所有上级分类即可 1.在分类模型中增加取出所有上级分类的方法 /********** [面包屑导航]取出一个分类所有上级分类 **********/ pub ...
- 《Java I/O 从0到1》 - 第Ⅰ滴血 File
前言 File 类的介绍主要会依据<Java 编程思想>以及官网API .相信大家在日常工作中,肯定会遇到文件流的读取等操作,但是在搜索过程中,并没有找到一个介绍的很简洁明了的文章.因此, ...
- 《C++ Primer》第II部分:C++标准库
<C++ Primer>第II部分:C++标准库 前言 把<C++ Primer>读薄系列笔记.本篇为第II部分C++标准库,包含全书第8-12章重难点: IO库 顺序容器 范 ...
- CakePHP中回调函数的使用
我们知道模型主要是用来处理数据的,有时我们想在模型操作之前或之后做一些额外逻辑处理,这时候就可以使用回调函数. 回调函数有很多种,beforeFind,afterFind,beforeValidate ...
- Qt-第一个QML程序-4-自定义按钮类,动画,状态
上篇中,我们写到了自己定义了一个按钮,但是呢,按照这样的写法,要写一个程序出来,那要累死了,所以,qml给我的感觉就是各种随便调用,所以了,可以自己写一个自己Button的qml,这样在以后用到了,就 ...
- 180611-Spring之RedisTemplate配置与使用
logo 文章链接:https://liuyueyi.github.io/hexblog/2018/06/11/180611-Spring之RedisTemplate配置与使用/ Spring ...
- 1.1.0 Unity零基础入门2——Roll a Ball
1. 游戏界面 2.代码 //FoodRotate - - 控制cube旋转 using System.Collections; using System.Collections.Generic; u ...
- 详解Jedis连接池报错处理
在使用Jedis连接池模式下,比较常见的报错如下: redis.clients.jedis.exceptions.JedisConnectionException:Could not get a re ...
- Python3 集合
1.集合的表示 集合是一个无序不重复的元素序列 创建空集合 set() 2.集合的运算 a={1,2,3} b={2,3,4} print(a-b) #a中包含b中不包含 print(a|b) #a中 ...
- RDL/RDLC批量单据打印 [转]
RDL/RDLC批量单据打印 使用RDL或RDLC进行单据打印时,单张单据打印比较直观简单,无需说明.下面我们来谈一下批量单据打印的实现方法.以下以RDL的ReportBuilder设计环境为例进行讲 ...