事先说明:这不是好题解,这只是思考记录

同机房的xp都学博弈了、、    感觉还是学学看吧

先来看一下简单的nim:

题目:

Alice和Bob放置了N堆不同的石子,编号1..N,第i堆中有A[i]个石子。
每一次行动,Alice和Bob可以选择从一堆石子中取出任意数量的石子。至少取1颗,至多取出这一堆剩下的所有石子。
Alice和Bob轮流行动,取走最后一个石子的人获得胜利。
假设每一轮游戏都是Alice先行动,请你判断在给定的情况下,如果双方都足够聪明,谁会获得胜利?

题目太长了!画个图压压惊:

如果你有博弈恐惧症,那你可以看一下比题目还短的代码来提振信心:

#include <bits/stdc++.h>using namespace std;int n, p, a;int main() {while (cin >> n) {for (int i = 0; i < n; ++i) {cin >> a;if (i == 0) p = a;else p ^= a;}if (p == 0) cout << "Bob" << endl;else cout << "Alice" << endl;}return 0;}

好了,开始学习预备知识:

首先认识一下公平组合游戏(ICG):

它的规则是:

1、有两名玩家(p1、p2)

2、每一个玩家可以动 游戏里的每个棋子(没有红方、黑方的棋子之分、 、“动”指和原来的局面不一样)

3、不许作弊、、没有buff、、没人开黑

4、动不了的选手输(所以游戏就是比谁拿走最后一个、、)

只有两个玩家非常机智才能叫博弈

当然,如果两个ZZ玩游戏,那就叫菜鸡互啄、、

所以两个玩家都做出最优的决定;;(你知道我的套路,我知道你的套路,如果我进入你的套路我就输了)

所以先手有主动权,可以一步使对方进入自己的套路

而有时,游戏一开始的局面就是对方的套路,那就相当于对方先把你套路了,然后你就没办法了、、

所以对于这类游戏,胜负取决于初始局面、、

研究这种局面的胜负 就叫狭义的 博弈论、、

所以局面分两类:

P-position:在当前的局面下,先手必败。
N-position:在当前的局面下,先手必胜。

他们有如下性质:
1.合法操作集合为空的局面是P-position;
2.可以移动到P-position的局面是N-position;
3.所有移动都只能到N-position的局面是P-position。

对于1:动不了的肯定输

对于2:这里的先手是相对的  这一回合你是先手,下一回合他是先手,如果你冰雪聪明是可以把下一个先手(对方,也就是这一个后手)套路的

如果你不聪明,那你玩游戏就不能叫博弈

对于3:无论如何下一个先手都赢了,你就只能gg了

根据性质3是可以递推的、、但一般写记搜+递归

所以,我们将p写作0,n写作1

所以  0前面都是1

1后面一定有0 、、囧

就像这个:

如果

对于上图的路径特点我们可以想到什么?

就是二进制的 异或^

但为什么要用每一堆的石子数 异或?

证明(来自百度百科):

第一个命题显然,terminal position只有一个,就是全0,异或仍然是0。
第二个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an<>0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。不妨设a1^a2^...^an=k,则一定存在某个ai,它的二进制表示在k的最高位上是1(否则k的最高位那个1是怎么得到的)。这时ai^k<ai一定成立。则我们可以将ai改变成ai'=ai^k,此时a1^a2^...^ai'^...^an=a1^a2^...^an^k=0。
第三个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。因为异或运算满足消去率,由a1^a2^...^an=a1^a2^...^ai'^...^an可以得到ai=ai'。所以将ai改变成ai'不是一个合法的移动。证毕。

我个人感觉这应该只是异或的性质和nim游戏的博弈法则不谋而合

所以等量代换了、、

sg函数:

它是博弈的核心;;;

它存的是每个状态(石子数);;

先来看几个简单的知识:

游戏的和:

如果每次游戏可以分成n个子游戏,每次只能操作其中1个子游戏:

f(总)=f(第一个子游戏)^f(第二个子游戏)^f(第三个子游戏)…………^f(第n个子游戏)

结合nim很好证明

下面几个图帮助理解:

由于每个子游戏相同,堆中数相同时输赢相同,可以简化为:

注意是两个棋子在起点

这就可以递归判断当前局面

有了这些知识和模型,平等博弈类的问题已经可以解答了

对于这个题:

我们先划分子游戏,以便合为这个分裂游戏:

每个堆的转移是会互相影响的(++--)

但是每个石子的转移相互独立(一个石子的转移不会影响另一个石子的转移)

所以每个石子都是一个游戏、、

在每一堆里,每个石子都有不同的sg值

一颗在i被取走的石子会在j、k加入一颗石子

我们注意到只有最后一个有石子的情况是P

也就是说所有的石子随着时间推移都会被扔到最后一个

所以当枚举堆数到了最后一堆时,就是terminal position(末端情况)

所以取走当前的石子游戏可以看成是往右的j、k位置的方案

另外我们注意到石子的具体个数是没有关系的,只与奇偶性有关(所有>1的偶数都早晚要被互拿转移为0,所以就当它是0,所有>1的奇数都会被拿为1,即交换了一次先后手,所以会对答案造成影响)

所以需要计算玩i这个石子(虽然一点都不好玩),也一定要玩到j、k  一直玩到n,,就一路异或即可

所以玩这个石子的情况=初始局面 -i的1  +j的1   +k的1、、

由于你可以玩石子数为奇数的堆数颗石子的取一+2游戏

所以总游戏就是  取这些堆的子游戏异或和^sg(i)^sg(j)^sg(k)(sg是一大串异或,是初始局面     由于异或的性质,+和-都可以用^表示)

码:

#include<iostream>
#include<cstdio>
using namespace std;
#include<cstring>
int n,j,i,k,f[30],T,x,ans,cnt;
bool  vis[40];
int getsg(int x)
{//cout<<x;
int j;if(x==n)return 0;//cout<<f[x]<<endl;if(f[x]!=-1)return f[x];memset(vis,0,sizeof(vis));for(int j=x+1;j<=n;j++)for(int k=j;k<=n;k++){vis[getsg(j)^getsg(k)]=1;// cout<<getsg(j)<<" "<<getsg(k)<<endl;  }for(j=0;vis[j];j++);//不要脑残的写:  for(int j;vis[j],j++); return f[x]=j;}int main()
{scanf("%d",&T);while(T--){ans=cnt=0;   memset(f,-1,sizeof(f));//一开始没清0 scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d",&x);if(x&1)ans^=getsg(i);//统计初始局面 //    cout<<ans<<endl;        }for(i=1;i<n;i++)for(j=i+1;j<=n;j++)for(k=j;k<=n;k++){if(!(ans^getsg(i)^getsg(j)^getsg(k)))if((++cnt)==1)printf("%d %d %d\n",i-1,j-1,k-1);}if (!cnt) puts("-1 -1 -1"); printf("%d\n",cnt);  //输出-1 -1 -1 隔开      输出方案总数  }  } 

2017.3.24 分裂游戏 思考记录相关推荐

  1. 2017.10.24 上升序列 思考记录

    终于有会做的题了... 一开始想用正常的lis ,然后从前往后扫, 由于最优查询区间在序列上是按顺序单调递增的,所以想记录每个点取哪个值跳到哪,这样是n*m logn的 然后发现既然是单调递增的那直接 ...

  2. 2017.10.13 硬币游戏 思考记录

    这个题用特征法可以比较好的找到思路 首先这个题有两个特征:1.正+反=反   反+正=反  正+正=正  反+反=正   应该能很快想到亦或 2.范围1e5  +  上限2^60   应该能很快想到倍 ...

  3. 2017.10.10 杀人游戏 思考记录

    这个题题目描述还是有一点问题..这里的知道关系是有传递性的.第一次理解成只会明白直接相连的,最小点覆盖  --   这样就成np了吧.. 所以就比较显然了,,对于每组能互达的强连通分量,都只需一个头上 ...

  4. 2017.9.24 虔诚的墓主人 思考记录

    这个题是纯信息处理题. 首先要明白,对墓地进行枚举是会T的,需要对常青树进行枚举 所以枚举的顺序也要注意:一定要按某种顺序 然后要明白,每一个常青树会对列和行都造成影响,对答案有贡献的点只会在两个点之 ...

  5. 2017.9.10 连续攻击游戏 思考记录

    这个题..可以把它当成结论题搞, 结论就是对于每个数字的出现次数,如果区间1~n内的出现次数都>=2,那么这个区间1~n内都会合法 这个结论很好证明,连到外面可以视为停止   外面连进来的可以视 ...

  6. 2017.4.27 道路游戏 思考记录

    这个题一开始把题读错了..而且读错三次(出这种语文题 简直毒瘤) 一开始认为成了可以一边买一边跑...结果发现是n^4 然后认为成了可以不移动结束就可以买....结果发现是n^3  于是全wa 然后认 ...

  7. 2017.10.7 括号序列 思考记录

    这个题看起来很简单,但细节比较麻烦.参考完别人的代码后才想出自己的解法的.. 一开始认为已匹配的括号是可以直接消的,所以就只维护了两个变量 但还有区间取反.. 由于和已配对的括号的顺序有关,所以不是很 ...

  8. 2017.9.28 约数研究 思考记录

    这个题一开始可能会想复杂, 然而它需要对答案的贡献进行归类 可以发现,一个约数对其倍数的数贡献是一样的,,所以可以考虑离散 所以直接对于每个数不好求,就可以考虑每个约数的贡献 而每个约数i就有n/i个 ...

  9. 2017.5.11 道路修建 思考记录

    这个线段树一眼秒了(主要是有前面的思考经验) 就是维护两个列对应线段上的形态.,比较好想的 但数据结构题有个特点--使你知道结构也不会做----会做也会很大概率爆零.... 所以数据结构题必须 小数据 ...

最新文章

  1. 干货,Wireshark使用技巧-过滤规则
  2. 微信小程序----日期时间选择器(自定义精确到分秒或时段)
  3. 【转帖】Windows下PostgreSQL安装图解
  4. 黑马Java架构师实战训练学习手册
  5. 用java编写保留两位小数_Java保留两位小数的几种写法总结
  6. linux mysql 性能提高,高手心得:提高MySQL性能的方法
  7. flask get 参数_Python web 用它5分钟以后,我放弃用了四年的 Flask
  8. QT开发(三十四)——QT多线程编程
  9. RHEL AS 5 安装MYSQL
  10. 面包板是神马东西?。。
  11. 0ops CTF/0CTF writeup
  12. 注销的计算机管理员如何恢复,十分钟后如何注销用户?Power Manager是这样做的!...
  13. js-beautify 不换行
  14. 数据结构--AVL树(全)
  15. 计算机视觉-论文阅读笔记-基于高性能检测器与表观特征的多目标跟踪
  16. jupyter notebook 的 hinterland 插件 设置 代码提示但是默认不选中
  17. python实现三级菜单_Python3.5实现的三级菜单功能示例
  18. 如何提高网站的页面加载速度
  19. 无聊之作——《随机组句》
  20. Netty聊天室(2):从0开始实战100w级流量应用

热门文章

  1. 红橙Darren视频笔记 启动不在清单文件注册的activity 安卓8有效
  2. Android笔记 xml补间动画
  3. zookeper安装_ZooKeeper的安装与部署
  4. 从零开始学keras之kaggle猫狗识别分类器
  5. 计算机绘图理论试题库,CAD理论试卷及答案「最新」
  6. 变频器说明书_图解变频器接线方法
  7. python3集合(set)
  8. ios uilabel 垂直居中_iOS – 让UILabel的文字顶部对齐[转载]
  9. mac磨皮插件_Adobe Pr 黑科技插件合集,一键安装,Mac+Win
  10. 华为p50 pro 鸿蒙,华为P50Pro确认!1英寸大底相机+首发鸿蒙:这才是华为最强实力...