2017.3.24 分裂游戏 思考记录
事先说明:这不是好题解,这只是思考记录
同机房的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 、、囧
就像这个:
如果
对于上图的路径特点我们可以想到什么?
就是二进制的 异或^
但为什么要用每一堆的石子数 异或?
证明(来自百度百科):
我个人感觉这应该只是异或的性质和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 分裂游戏 思考记录相关推荐
- 2017.10.24 上升序列 思考记录
终于有会做的题了... 一开始想用正常的lis ,然后从前往后扫, 由于最优查询区间在序列上是按顺序单调递增的,所以想记录每个点取哪个值跳到哪,这样是n*m logn的 然后发现既然是单调递增的那直接 ...
- 2017.10.13 硬币游戏 思考记录
这个题用特征法可以比较好的找到思路 首先这个题有两个特征:1.正+反=反 反+正=反 正+正=正 反+反=正 应该能很快想到亦或 2.范围1e5 + 上限2^60 应该能很快想到倍 ...
- 2017.10.10 杀人游戏 思考记录
这个题题目描述还是有一点问题..这里的知道关系是有传递性的.第一次理解成只会明白直接相连的,最小点覆盖 -- 这样就成np了吧.. 所以就比较显然了,,对于每组能互达的强连通分量,都只需一个头上 ...
- 2017.9.24 虔诚的墓主人 思考记录
这个题是纯信息处理题. 首先要明白,对墓地进行枚举是会T的,需要对常青树进行枚举 所以枚举的顺序也要注意:一定要按某种顺序 然后要明白,每一个常青树会对列和行都造成影响,对答案有贡献的点只会在两个点之 ...
- 2017.9.10 连续攻击游戏 思考记录
这个题..可以把它当成结论题搞, 结论就是对于每个数字的出现次数,如果区间1~n内的出现次数都>=2,那么这个区间1~n内都会合法 这个结论很好证明,连到外面可以视为停止 外面连进来的可以视 ...
- 2017.4.27 道路游戏 思考记录
这个题一开始把题读错了..而且读错三次(出这种语文题 简直毒瘤) 一开始认为成了可以一边买一边跑...结果发现是n^4 然后认为成了可以不移动结束就可以买....结果发现是n^3 于是全wa 然后认 ...
- 2017.10.7 括号序列 思考记录
这个题看起来很简单,但细节比较麻烦.参考完别人的代码后才想出自己的解法的.. 一开始认为已匹配的括号是可以直接消的,所以就只维护了两个变量 但还有区间取反.. 由于和已配对的括号的顺序有关,所以不是很 ...
- 2017.9.28 约数研究 思考记录
这个题一开始可能会想复杂, 然而它需要对答案的贡献进行归类 可以发现,一个约数对其倍数的数贡献是一样的,,所以可以考虑离散 所以直接对于每个数不好求,就可以考虑每个约数的贡献 而每个约数i就有n/i个 ...
- 2017.5.11 道路修建 思考记录
这个线段树一眼秒了(主要是有前面的思考经验) 就是维护两个列对应线段上的形态.,比较好想的 但数据结构题有个特点--使你知道结构也不会做----会做也会很大概率爆零.... 所以数据结构题必须 小数据 ...
最新文章
- 干货,Wireshark使用技巧-过滤规则
- 微信小程序----日期时间选择器(自定义精确到分秒或时段)
- 【转帖】Windows下PostgreSQL安装图解
- 黑马Java架构师实战训练学习手册
- 用java编写保留两位小数_Java保留两位小数的几种写法总结
- linux mysql 性能提高,高手心得:提高MySQL性能的方法
- flask get 参数_Python web 用它5分钟以后,我放弃用了四年的 Flask
- QT开发(三十四)——QT多线程编程
- RHEL AS 5 安装MYSQL
- 面包板是神马东西?。。
- 0ops CTF/0CTF writeup
- 注销的计算机管理员如何恢复,十分钟后如何注销用户?Power Manager是这样做的!...
- js-beautify 不换行
- 数据结构--AVL树(全)
- 计算机视觉-论文阅读笔记-基于高性能检测器与表观特征的多目标跟踪
- jupyter notebook 的 hinterland 插件 设置 代码提示但是默认不选中
- python实现三级菜单_Python3.5实现的三级菜单功能示例
- 如何提高网站的页面加载速度
- 无聊之作——《随机组句》
- Netty聊天室(2):从0开始实战100w级流量应用
热门文章
- 红橙Darren视频笔记 启动不在清单文件注册的activity 安卓8有效
- Android笔记 xml补间动画
- zookeper安装_ZooKeeper的安装与部署
- 从零开始学keras之kaggle猫狗识别分类器
- 计算机绘图理论试题库,CAD理论试卷及答案「最新」
- 变频器说明书_图解变频器接线方法
- python3集合(set)
- ios uilabel 垂直居中_iOS – 让UILabel的文字顶部对齐[转载]
- mac磨皮插件_Adobe Pr 黑科技插件合集,一键安装,Mac+Win
- 华为p50 pro 鸿蒙,华为P50Pro确认!1英寸大底相机+首发鸿蒙:这才是华为最强实力...