好久没写oi系列的题解了

要不是为了大作业我才不会回来学这些奇怪的东西呢

本题对抗搜索就好啦

首先要分析一点,就是由于我们的黑棋每次走两步,白棋只走一步而且是白棋先走,所以除非白棋第一步吃掉黑棋,否则黑棋必胜

接下来就是计算黑棋如何取胜的问题了

首先简单介绍一下对抗搜索

我们知道,两个人下棋,两个人都想赢(或者至少不想输得那么惨),那么这个问题可以转化成——第一个人想赢,而第二个人想让第一个人输(或者赢得不容易)

这就是对抗搜索得思想:如果我们对每个局面给出一个估值,估值越大表示第一个人越优,那么在第一个人下棋的时候,他的目的是要使当前局面的估值最大

而对第二个人而言,他的目的是使当前局面的估值最小

于是我们利用dfs+记忆化来实现就可以了

注意这里反一下,黑棋要使自己的步数最小

这里记忆化要记录步数,其原因是有可能这两个人出现来回交换位置转圈的情况,这种情况要通过步数来区分

  1. 1 #include <cstdio>
  2. 2 #include <cmath>
  3. 3 #include <cstring>
  4. 4 #include <cstdlib>
  5. 5 #include <iostream>
  6. 6 #include <algorithm>
  7. 7 #include <queue>
  8. 8 #include <stack>
  9. 9 using namespace std;
  10. 10 const int inf=1e9;
  11. 11 int x1,x2,yy,y2;
  12. 12 int n;
  13. 13 int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
  14. 14 int ret[21][21][21][21][61][2];
  15. 15 bool check(int x,int y)
  16. 16 {
  17. 17 return x>0&&x<=n&&y>0&&y<=n;
  18. 18 }
  19. 19 int dfs(int xw,int yw,int xb,int yb,int typ,int dep)//typ=1表示黑棋走,typ=0表示白棋走
  20. 20 {
  21. 21 if(dep>3*n)return inf;
  22. 22 if(ret[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
  23. 23 if(xw==xb&&yw==yb)
  24. 24 {
  25. 25 if(typ)return inf;
  26. 26 else return 0;
  27. 27 }
  28. 28 int temp=typ?inf:0;
  29. 29 if(typ)
  30. 30 {
  31. 31 for(int i=1;i<=8;i++)
  32. 32 {
  33. 33 int tx=xb+to[i][0],ty=yb+to[i][1];
  34. 34 if(check(tx,ty))temp=min(temp,dfs(xw,yw,tx,ty,typ^1,dep+1));
  35. 35 }
  36. 36 }else
  37. 37 {
  38. 38 for(int i=1;i<=4;i++)
  39. 39 {
  40. 40 int tx=xw+to[i][0],ty=yw+to[i][1];
  41. 41 if(check(tx,ty))temp=max(temp,dfs(tx,ty,xb,yb,typ^1,dep+1));
  42. 42 }
  43. 43 }
  44. 44 return ret[xw][yw][xb][yb][dep][typ]=temp+1;
  45. 45 }
  46. 46 int main()
  47. 47 {
  48. 48 scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
  49. 49 if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1\n");return 0;}
  50. 50 else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1));
  51. 51 return 0;
  52. 52 }

当然,这里也可以使用$\alpha$-$\beta$剪枝,其原理如下:

考虑一个黑棋下的局面,我们知道他的前一手和后一手都是白棋要下的(废话)

如果这个局面有一个发展使得黑棋可以以a步抓到白棋,那么这个局面黑棋取胜所需的最多步数即为$a$

可是我们知道,对于当前局面的上一层,是一个白棋的局面,他想最大化黑棋的步数,假设白棋已经搜到的前几个局面之中黑棋最大的步数是$b$

那我们可以发现,如果$a<b$,那么无论当前局面如何发展,白棋都不会容许走到当前局面(思考一下:当双方均采用最优策略时,如果进入这个局面,黑棋至多只需要$a$步就能取胜,而如果不进入这个局面,黑棋至少需要$b$步才能取胜,所以白棋一定不会允许黑棋进入这个局面,也即我们不再需要了解这个局面接下来会如何演变,因为白棋一定不会选择这个局面!)

于是同理考虑一个白棋下的情况,同样进行剪枝就可以了

  1. 1 #include <cstdio>
  2. 2 #include <cmath>
  3. 3 #include <cstring>
  4. 4 #include <cstdlib>
  5. 5 #include <iostream>
  6. 6 #include <algorithm>
  7. 7 #include <queue>
  8. 8 #include <stack>
  9. 9 using namespace std;
  10. 10 const int inf=1e9;
  11. 11 int x1,x2,yy,y2;
  12. 12 int n;
  13. 13 int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
  14. 14 int ret[21][21][21][21][61][2];
  15. 15 bool used[21][21][21][21][61][2];
  16. 16 bool check(int x,int y)
  17. 17 {
  18. 18 return x>0&&x<=n&&y>0&&y<=n;
  19. 19 }
  20. 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. 21 {
  22. 22 if(dep>3*n)return inf;
  23. 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. 24 if(xw==xb&&yw==yb)
  25. 25 {
  26. 26 if(typ)return inf;
  27. 27 else return 0;
  28. 28 }
  29. 29 int temp=typ?inf:0;
  30. 30 ret[xw][yw][xb][yb][dep][typ]=temp;
  31. 31 if(typ)
  32. 32 {
  33. 33 for(int i=1;i<=8;i++)
  34. 34 {
  35. 35 int tx=xb+to[i][0],ty=yb+to[i][1];
  36. 36 if(check(tx,ty))temp=min(temp,dfs(xw,yw,tx,ty,typ^1,dep+1,xw,yw,xb,yb));
  37. 37 ret[xw][yw][xb][yb][dep][typ]=temp+1;
  38. 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. 39 }
  40. 40 }else
  41. 41 {
  42. 42 for(int i=1;i<=4;i++)
  43. 43 {
  44. 44 int tx=xw+to[i][0],ty=yw+to[i][1];
  45. 45 if(check(tx,ty))temp=max(temp,dfs(tx,ty,xb,yb,typ^1,dep+1,xw,yw,xb,yb));
  46. 46 ret[xw][yw][xb][yb][dep][typ]=temp+1;
  47. 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. 48 }
  49. 49 }
  50. 50 used[xw][yw][xb][yb][dep][typ]=0;
  51. 51 return ret[xw][yw][xb][yb][dep][typ]=temp+1;
  52. 52 }
  53. 53 int main()
  54. 54 {
  55. 55 scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
  56. 56 if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1\n");return 0;}
  57. 57 else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1,0,0,0,0));
  58. 58 return 0;
  59. 59 }

其实你会发现,剪枝的版本比不剪枝的版本还要慢,究其原因,在于我们剪枝让一部分记忆化失效了,这样反而增加了用时

如果用下面的写法就可以回避这个问题,但是由于增加了一个数组会造成mle

  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. int used[21][21][21][21][61][2];
  16. int max(int x,int y)
  17. {
  18. return x>y?x:y;
  19. }
  20. int min(int x,int y)
  21. {
  22. return x<y?x:y;
  23. }
  24. bool check(int x,int y)
  25. {
  26. return x>0&&x<=n&&y>0&&y<=n;
  27. }
  28. 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表示白棋走
  29. {
  30. if(dep>3*n)return inf;
  31. if(ret[xw][yw][xb][yb][dep][typ]&&!used[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
  32. if(xw==xb&&yw==yb)
  33. {
  34. if(typ)return inf;
  35. else return 0;
  36. }
  37. int temp=typ?inf:0;
  38. if(!ret[xw][yw][xb][yb][dep][typ])ret[xw][yw][xb][yb][dep][typ]=temp;
  39. if(typ)
  40. {
  41. for(int i=used[xw][yw][xb][yb][dep][typ]+1;i<=8;i++)
  42. {
  43. int tx=xb+to[i][0],ty=yb+to[i][1];
  44. if(check(tx,ty)){int t=dfs(xw,yw,tx,ty,typ^1,dep+1,xw,yw,xb,yb);temp=min(temp,t);}
  45. ret[xw][yw][xb][yb][dep][typ]=min(ret[xw][yw][xb][yb][dep][typ],temp+1);
  46. 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;}
  47. }
  48. }else
  49. {
  50. for(int i=used[xw][yw][xb][yb][dep][typ]+1;i<=4;i++)
  51. {
  52. int tx=xw+to[i][0],ty=yw+to[i][1];
  53. if(check(tx,ty)){int t=dfs(tx,ty,xb,yb,typ^1,dep+1,xw,yw,xb,yb);temp=max(temp,t);}
  54. ret[xw][yw][xb][yb][dep][typ]=max(ret[xw][yw][xb][yb][dep][typ],temp+1);
  55. 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;}
  56. }
  57. }
  58. used[xw][yw][xb][yb][dep][typ]=0;
  59. return ret[xw][yw][xb][yb][dep][typ];
  60. }
  61. int main()
  62. {
  63. scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
  64. if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1\n");return 0;}
  65. else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1,0,0,0,0));
  66. return 0;
  67. }

bzoj 3106的更多相关文章

  1. BZOJ 3106 棋盘游戏

    Description 一个\(n \times n(n \le 2)\)棋盘上有黑白棋子各一枚.游戏者A和B轮流移动棋子,A先走. A的移动规则:只能移动白棋子.可以往上下左右四个方向之一移动一格. ...

  2. BZOJ 3106: [cqoi2013]棋盘游戏(对抗搜索)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3106 对抗搜索,f[x][y][a][b][c][d]表示当前谁走,走了几步,及位置. (因为 ...

  3. 【BZOJ 3106】 3106: [cqoi2013]棋盘游戏 (对抗搜索)

    3106: [cqoi2013]棋盘游戏 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 544  Solved: 233 Description 一个 ...

  4. BZOJ 3106: [cqoi2013]棋盘游戏

    Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 859  Solved: 356[Submit][Status][Discuss] Descriptio ...

  5. BZOJ 2127: happiness [最小割]

    2127: happiness Time Limit: 51 Sec  Memory Limit: 259 MBSubmit: 1815  Solved: 878[Submit][Status][Di ...

  6. BZOJ 3275: Number

    3275: Number Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 874  Solved: 371[Submit][Status][Discus ...

  7. BZOJ 2879: [Noi2012]美食节

    2879: [Noi2012]美食节 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1834  Solved: 969[Submit][Status] ...

  8. bzoj 4610 Ceiling Functi

    bzoj 4610 Ceiling Functi Description bzoj上的描述有问题 给出\(n\)个长度为\(k\)的数列,将每个数列构成一个二叉搜索树,问有多少颗形态不同的树. Inp ...

  9. BZOJ 题目整理

    bzoj 500题纪念 总结一发题目吧,挑几道题整理一下,(方便拖板子) 1039:每条线段与前一条线段之间的长度的比例和夹角不会因平移.旋转.放缩而改变,所以将每条轨迹改为比例和夹角的序列,复制一份 ...

  10. 【sdoi2013】森林 BZOJ 3123

    Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数 ...

随机推荐

  1. STP理论基础

    目的 防止二层环路及防止环路造成的广播风暴以及引起的MAC表震荡 方法 首先,所有配置了STP的交换机互相向相邻交换器(配置了STP的)发送BPDU(协议数据单元),选举根桥(根交换机),根交换机上所 ...

  2. 谈谈mysql的主键和外键

    主键:保证数据的唯一性,非空且唯一,一般设置主键的语法为:字段 类型 PRIMARY KEY;或者 字段 类型,PRIMARY KEY(字段名) 外键:保证数据的完整性,一致性.一般设置的外键关联的是 ...

  3. 直播带货源码,flutter 顶部滚动栏+页面

    直播带货源码,flutter 顶部滚动栏+页面 tabPage.dart import 'package:flutter/cupertino.dart';import 'package:flutter ...

  4. GO 语言中的 sync Map

    为什么需要 sync map go 语言之所以引入 sync.Map主要是因为GO 语言自带的 map 是线程不安全的.只能保证并发的读,但是不能保证并发的写. 看下面的例子: func main() ...

  5. 学习-Vue2-Vue实例-数据与方法-数据的响应式

    当一个实例被创建时,它将data对象中的所有的property加入到Vue的响应式系统中. 当这些property的值发生改变时,视图将会产生"响应",即匹配更新为新的值.当这些数 ...

  6. ubuntu usb network card drive

    通过 lsusb -t命令查看网卡型号 /: Bus 02.Port 1: Dev 1, class="root_hub", Driver=xhci_hcd/4p, 5000M | ...

  7. python学习:sqlite3 文件型数据库

    摘录:https://www.cnblogs.com/decwang/p/4565572.html SQLite 字段类型 一般数据采用的固定的静态数据类型,而SQLite采用的是动态数据类型,会根据 ...

  8. for in | for in 比较 解释 | 以后找知识点先从这里面搜索

    const obj = { a: 1, b: 2, c: 3 } for (let i in obj) { console.log(i) // a // b // c } for (let i of ...

  9. MySQL 5.7升级8.0过程(详解)

    记一次MySQL 5.7升级8.0的详细过程,聊聊我的思路,希望可以帮助大家. 以一个例子为切入点 一.升级背景 为什么要升级到MySQL8.0?大概多久进行一次? 大家可以参考下图记录的各个版本的发 ...

  10. MulVAL攻击图的推理规则

    MulVAL ( multihost, multistage, vulnerability analysis) 是由普林斯顿大学的 Ou 等开发的 Linux 平台开源攻击图生成工具,基于 Nessu ...