bzoj 3106
好久没写oi系列的题解了
要不是为了大作业我才不会回来学这些奇怪的东西呢
本题对抗搜索就好啦
首先要分析一点,就是由于我们的黑棋每次走两步,白棋只走一步而且是白棋先走,所以除非白棋第一步吃掉黑棋,否则黑棋必胜
接下来就是计算黑棋如何取胜的问题了
首先简单介绍一下对抗搜索
我们知道,两个人下棋,两个人都想赢(或者至少不想输得那么惨),那么这个问题可以转化成——第一个人想赢,而第二个人想让第一个人输(或者赢得不容易)
这就是对抗搜索得思想:如果我们对每个局面给出一个估值,估值越大表示第一个人越优,那么在第一个人下棋的时候,他的目的是要使当前局面的估值最大
而对第二个人而言,他的目的是使当前局面的估值最小
于是我们利用dfs+记忆化来实现就可以了
注意这里反一下,黑棋要使自己的步数最小
这里记忆化要记录步数,其原因是有可能这两个人出现来回交换位置转圈的情况,这种情况要通过步数来区分
1 #include <cstdio>
2 #include <cmath>
3 #include <cstring>
4 #include <cstdlib>
5 #include <iostream>
6 #include <algorithm>
7 #include <queue>
8 #include <stack>
9 using namespace std;
10 const int inf=1e9;
11 int x1,x2,yy,y2;
12 int n;
13 int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
14 int ret[21][21][21][21][61][2];
15 bool check(int x,int y)
16 {
17 return x>0&&x<=n&&y>0&&y<=n;
18 }
19 int dfs(int xw,int yw,int xb,int yb,int typ,int dep)//typ=1表示黑棋走,typ=0表示白棋走
20 {
21 if(dep>3*n)return inf;
22 if(ret[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
23 if(xw==xb&&yw==yb)
24 {
25 if(typ)return inf;
26 else return 0;
27 }
28 int temp=typ?inf:0;
29 if(typ)
30 {
31 for(int i=1;i<=8;i++)
32 {
33 int tx=xb+to[i][0],ty=yb+to[i][1];
34 if(check(tx,ty))temp=min(temp,dfs(xw,yw,tx,ty,typ^1,dep+1));
35 }
36 }else
37 {
38 for(int i=1;i<=4;i++)
39 {
40 int tx=xw+to[i][0],ty=yw+to[i][1];
41 if(check(tx,ty))temp=max(temp,dfs(tx,ty,xb,yb,typ^1,dep+1));
42 }
43 }
44 return ret[xw][yw][xb][yb][dep][typ]=temp+1;
45 }
46 int main()
47 {
48 scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
49 if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1\n");return 0;}
50 else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1));
51 return 0;
52 }
当然,这里也可以使用$\alpha$-$\beta$剪枝,其原理如下:
考虑一个黑棋下的局面,我们知道他的前一手和后一手都是白棋要下的(废话)
如果这个局面有一个发展使得黑棋可以以a步抓到白棋,那么这个局面黑棋取胜所需的最多步数即为$a$
可是我们知道,对于当前局面的上一层,是一个白棋的局面,他想最大化黑棋的步数,假设白棋已经搜到的前几个局面之中黑棋最大的步数是$b$
那我们可以发现,如果$a<b$,那么无论当前局面如何发展,白棋都不会容许走到当前局面(思考一下:当双方均采用最优策略时,如果进入这个局面,黑棋至多只需要$a$步就能取胜,而如果不进入这个局面,黑棋至少需要$b$步才能取胜,所以白棋一定不会允许黑棋进入这个局面,也即我们不再需要了解这个局面接下来会如何演变,因为白棋一定不会选择这个局面!)
于是同理考虑一个白棋下的情况,同样进行剪枝就可以了
1 #include <cstdio>
2 #include <cmath>
3 #include <cstring>
4 #include <cstdlib>
5 #include <iostream>
6 #include <algorithm>
7 #include <queue>
8 #include <stack>
9 using namespace std;
10 const int inf=1e9;
11 int x1,x2,yy,y2;
12 int n;
13 int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
14 int ret[21][21][21][21][61][2];
15 bool used[21][21][21][21][61][2];
16 bool check(int x,int y)
17 {
18 return x>0&&x<=n&&y>0&&y<=n;
19 }
20 int dfs(int xw,int yw,int xb,int yb,int typ,int dep,int lasxw,int lasyw,int lasxb,int lasyb)//typ=1表示黑棋走,typ=0表示白棋走
21 {
22 if(dep>3*n)return inf;
23 if(ret[xw][yw][xb][yb][dep][typ]&&!used[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
24 if(xw==xb&&yw==yb)
25 {
26 if(typ)return inf;
27 else return 0;
28 }
29 int temp=typ?inf:0;
30 ret[xw][yw][xb][yb][dep][typ]=temp;
31 if(typ)
32 {
33 for(int i=1;i<=8;i++)
34 {
35 int tx=xb+to[i][0],ty=yb+to[i][1];
36 if(check(tx,ty))temp=min(temp,dfs(xw,yw,tx,ty,typ^1,dep+1,xw,yw,xb,yb));
37 ret[xw][yw][xb][yb][dep][typ]=temp+1;
38 if(temp+1<ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=1;return temp+1;}
39 }
40 }else
41 {
42 for(int i=1;i<=4;i++)
43 {
44 int tx=xw+to[i][0],ty=yw+to[i][1];
45 if(check(tx,ty))temp=max(temp,dfs(tx,ty,xb,yb,typ^1,dep+1,xw,yw,xb,yb));
46 ret[xw][yw][xb][yb][dep][typ]=temp+1;
47 if(temp+1>ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=1;return temp+1;}
48 }
49 }
50 used[xw][yw][xb][yb][dep][typ]=0;
51 return ret[xw][yw][xb][yb][dep][typ]=temp+1;
52 }
53 int main()
54 {
55 scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
56 if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1\n");return 0;}
57 else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1,0,0,0,0));
58 return 0;
59 }
其实你会发现,剪枝的版本比不剪枝的版本还要慢,究其原因,在于我们剪枝让一部分记忆化失效了,这样反而增加了用时
如果用下面的写法就可以回避这个问题,但是由于增加了一个数组会造成mle
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
const int inf=1e9;
int x1,x2,yy,y2;
int n;
int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
int ret[21][21][21][21][61][2];
int used[21][21][21][21][61][2];
int max(int x,int y)
{
return x>y?x:y;
}
int min(int x,int y)
{
return x<y?x:y;
}
bool check(int x,int y)
{
return x>0&&x<=n&&y>0&&y<=n;
}
int dfs(int xw,int yw,int xb,int yb,int typ,int dep,int lasxw,int lasyw,int lasxb,int lasyb)//typ=1表示黑棋走,typ=0表示白棋走
{
if(dep>3*n)return inf;
if(ret[xw][yw][xb][yb][dep][typ]&&!used[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
if(xw==xb&&yw==yb)
{
if(typ)return inf;
else return 0;
}
int temp=typ?inf:0;
if(!ret[xw][yw][xb][yb][dep][typ])ret[xw][yw][xb][yb][dep][typ]=temp;
if(typ)
{
for(int i=used[xw][yw][xb][yb][dep][typ]+1;i<=8;i++)
{
int tx=xb+to[i][0],ty=yb+to[i][1];
if(check(tx,ty)){int t=dfs(xw,yw,tx,ty,typ^1,dep+1,xw,yw,xb,yb);temp=min(temp,t);}
ret[xw][yw][xb][yb][dep][typ]=min(ret[xw][yw][xb][yb][dep][typ],temp+1);
if(temp+1<ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=i;return temp+1;}
}
}else
{
for(int i=used[xw][yw][xb][yb][dep][typ]+1;i<=4;i++)
{
int tx=xw+to[i][0],ty=yw+to[i][1];
if(check(tx,ty)){int t=dfs(tx,ty,xb,yb,typ^1,dep+1,xw,yw,xb,yb);temp=max(temp,t);}
ret[xw][yw][xb][yb][dep][typ]=max(ret[xw][yw][xb][yb][dep][typ],temp+1);
if(temp+1>ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=i;return temp+1;}
}
}
used[xw][yw][xb][yb][dep][typ]=0;
return ret[xw][yw][xb][yb][dep][typ];
}
int main()
{
scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1\n");return 0;}
else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1,0,0,0,0));
return 0;
}
bzoj 3106的更多相关文章
- BZOJ 3106 棋盘游戏
Description 一个\(n \times n(n \le 2)\)棋盘上有黑白棋子各一枚.游戏者A和B轮流移动棋子,A先走. A的移动规则:只能移动白棋子.可以往上下左右四个方向之一移动一格. ...
- BZOJ 3106: [cqoi2013]棋盘游戏(对抗搜索)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3106 对抗搜索,f[x][y][a][b][c][d]表示当前谁走,走了几步,及位置. (因为 ...
- 【BZOJ 3106】 3106: [cqoi2013]棋盘游戏 (对抗搜索)
3106: [cqoi2013]棋盘游戏 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 544 Solved: 233 Description 一个 ...
- BZOJ 3106: [cqoi2013]棋盘游戏
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 859 Solved: 356[Submit][Status][Discuss] Descriptio ...
- BZOJ 2127: happiness [最小割]
2127: happiness Time Limit: 51 Sec Memory Limit: 259 MBSubmit: 1815 Solved: 878[Submit][Status][Di ...
- BZOJ 3275: Number
3275: Number Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 874 Solved: 371[Submit][Status][Discus ...
- BZOJ 2879: [Noi2012]美食节
2879: [Noi2012]美食节 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1834 Solved: 969[Submit][Status] ...
- bzoj 4610 Ceiling Functi
bzoj 4610 Ceiling Functi Description bzoj上的描述有问题 给出\(n\)个长度为\(k\)的数列,将每个数列构成一个二叉搜索树,问有多少颗形态不同的树. Inp ...
- BZOJ 题目整理
bzoj 500题纪念 总结一发题目吧,挑几道题整理一下,(方便拖板子) 1039:每条线段与前一条线段之间的长度的比例和夹角不会因平移.旋转.放缩而改变,所以将每条轨迹改为比例和夹角的序列,复制一份 ...
- 【sdoi2013】森林 BZOJ 3123
Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数 ...
随机推荐
- 一套 .NET开发的邮箱Mail开源库
今天给大家推荐一个基于.Net开发的邮箱开源库. 邮箱在我们日常工作中,可以说是非常常见了.个人邮箱一般都是免费的,但企业邮箱会收费,虽然一般情况下,市面邮箱已经够用了.但有些企业对内容安全要求比较严 ...
- 24js Number(数字)对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- 记一次redis集群搭建过程
在搭建前,我们先用vmware创建3台虚拟机,并确保它们相互之间能够ping通. 1. redis源码安装 1.1 编译安装 apt install gcc make wget http://down ...
- Winform窗体中打开PDF文件的三种方式
来源:https://www.jb51.net/article/251451.htm
- ASR6601:国产化lora SOC芯片兼容SX1262/SX1268
ASR6601为目前首颗国产化支持LoRaWAN低功耗广域网无线通信SoC芯片.ASR6601在单芯片上集成了通用微控制器和射频单元(SX1262),包括射频收发器,调制解调器和48 MHz 主频.A ...
- ORA-01653:unable to extend table xxxxx by 8192 in tablespace xxxxx
原因:表空间满 解决:扩展表空间 报错截图: 参考摘录:https://blog.csdn.net/qq_35257875/article/details/90295272
- git submodule .gitmodules 子模块
初始化 git submodule init 添加子项目 git submodule add -b xxxx {path} -b 指定分支 path 可为空 (不知为何, 直接写在 .gitmodul ...
- 关于htpasswd
什么是 htpasswd htpasswd是一个apache的内置工具,其生成的文件称之为htpasswd文件.htpasswd文件本身一个密码本,或者类似于数据库一样,用来存储一些密码(凭证)信息. ...
- mybatis_01 初运行
maven坐标 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</ ...
- js——带暂停、启动功能的定时
简单的封装,将 interval 二次封装,对外提供暂停.启动功能. 不足之处:interval定时间隔是固定的,在调用异步函数的时候,可能会出现bug.例如:在调用ajax异步请求过程中,发送a.b ...