【集训笔记】博弈论相关知识【HDOJ 1850【HDOJ2147
以下资料来自:http://blog.csdn.net/Dinosoft/article/details/6795700 http://qianmacao.blog.163.com/blog/static/203397180201222945212647/
http://blog.csdn.net/qinmusiyan/article/details/7950642#
HDU 1850 Being a Good Boy in Spring Festival
下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);
两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;
桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”
1、如果a1^a2^a3^...^an =0(即:nim - sum =0),说明先手没有必胜策略,方法数肯定为0;
2、假设先手有必胜策略。
问题则转化为=>在任意一堆拿走任意K张牌,并且剩下所有堆nim - sum =0(P-position)的方案总数。
①现在我们先看一个例子(5、7、9),并假设从第一堆取任意K张牌。
排除第一堆牌的nim - sum 为7^9=14
0111
^1001
--------------
1110
如果要使所有堆的nim-sum =0成立,则第一堆取掉K张以后必定为1110,因为X^X=0.
所以要观察5- K =15(K >0)成立,此例子(在第一堆取任意K张牌)明显不成立。
但不代表在第二或第三堆取任意K张牌的解不成立。
②现在看第二例子(15、7、9),并假设从第一堆取任意K张牌。
排除第一堆的nim - sum 为7^9=14,和第一个例子相同,所以问题变为观察15- K =14(K >0)是否成立。
显然这个例子成立。
3、总结得出:
在任意一堆拿任意K张牌,并且所有堆的nim-sum =0成立的条件为:
排除取掉K张牌的那一堆的nim-sum必须少于该堆牌上的数量(例子②),
否则不能在此堆上取任意K张牌所有堆的nim-sum =0成立(例子①)
故总方案数为(在任意一堆拿任意K张牌,并且所有堆的nim-sum =0成立)的总数。
#include <stdio.h> int main(){
int i,n,sum,a[];
while(EOF != scanf("%d",&n)){
if(n == )
break;
sum = ;
for(i = ;i < n;i++){
scanf("%d",&a[i]);
sum ^= a[i];
}
if(sum == )
printf("0\n");
else{
int count = ;
for(i=;i<n;i++){
sum ^= a[i];
if(a[i] > sum)
count++;
sum ^= a[i];
}
printf("%d\n",count);
}
}
return ;
}
hdu-2147:kiki's game
Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes the checkerboard game.The size of the chesserboard is n*m.First of all, a coin is placed in the top right corner(,m). Each time one people can move the coin into the left, the underneath or the left-underneath blank space.The person who can't make a move will lose the game. kiki plays it with ZZ.The game always starts with kiki. If both play perfectly, who will win the game? Input
Input contains multiple test cases. Each line contains two integer n, m (<n,m<=). The input is terminated when n= and m=. Output
If kiki wins the game printf "Wonderful!", else "What a pity!". Sample Input
Promblem description
P点:就是P个石子的时候,对方拿可以赢(自己输的)
N点:就是N个石子的时候,自己拿可以赢
现在关于P,N的求解有三个规则
(1):最终态都是P
(2):按照游戏规则,到达当前态的前态都是N的话,当前态是P
(3):按照游戏规则,到达当前态的前态至少有一个P的话,当前态是N
题意:
在一个m*n的棋盘内,从(1,m)点出发,每次可以进行的移动是:左移一,下移一,左下移一。然后kiki每次先走,判断kiki时候会赢(对方无路可走的时候)。
我们可以把PN状态的点描绘出来::
代码很简单,只需要判断奇偶
J.xiaodao 我爱你! |
|||||
|
|||||
Description |
|||||
自从见到 xiaodao 的第一眼起,我就不可救药的爱上了她。 xiaodao——如果四个顶点有石子,那么你可以把石子都拿走(不能剩下),如果没有石子你得再选一次,直到有拿走石子为止。 xiaodao——我们规定,如果谁没有石子拿了,谁就输,听明白了么! |
|||||
Input |
|||||
第一行是一个整数 T 代表以下 T 组数据。 每组数据占一行,两个整数 N , M。(注意,当 N * M 为奇数的时候中心的那一个石子已经被拿走了) |
|||||
Output |
|||||
输出一行,如果是 DS 获胜那么输出 "DS" , 否则输出 "xiaodao" . (不带引号) |
|||||
Sample Input |
|||||
2 2 2 3 3 |
|||||
Sample Output |
|||||
DS xiaodao |
|||||
Hint |
|||||
1. 以下是两个样例的游戏开始的示意图 2. 对于非法情况的界定 如图,对于下图的N=9,M=9情况,√的代表合法操作,×的代表非合法操作。 非合法操作有两种——超出边界或者格子四个顶点没有石子。 |
代码很简短,只需要判断奇偶性
#include<stdio.h>
int main(){
int t,m,n;
scanf("%d",&t);
while(t--){
scanf("%d%d",&m,&n);
if(n%==&&m%==)
printf("DS\n");
else
printf("xiaodao\n");
}
return ;
}
//文绉绉的定义,随便看看就好
Nim游戏是组合游戏(Combinatorial Games)的一种,准确来说,属于“Impartial Combinatori Games”(以下简称 ICG)。满足以下条件的游戏是ICG(可能不太严谨):
1、有两名选手;
2、两名选手交替对游戏进行移动(move),每次一步,选手可以在(一般而言)有限的合法移动集合中任选一种进行移动;
3、对于游戏的任何一种可能的局面,合法的移动集合只取决于这个局面本身,不取决于轮到哪名选手操作、以前的任何操作、骰子的点数或者其它什么因素;
4、如果轮到某名选手移动,且这个局面的合法的移动集合为空(也就是说此时无法进行移动),则这名选手负。根据这个定义,很多日常的游戏并非 ICG。例如象棋就不满
足条件3,因为红方只能移动红子,黑方只能移动黑子,合法的移动集合取决于轮到哪名选手操作。
Nim 游戏
定义:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
当然,特殊情况就是只有一堆或者两堆石子,自己去意淫一下吧。
P-position和N-position
其中P 代表Previous,N 代表Next。 P-position是必败态,N-position是必胜态。
必败(必胜)点属性
(1) 所有终结点是必败点(P点); //容易理解
(2) 从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点); //就是那种我们要走的方法
(3)无论如何操作, 从必败点(P点)都只能进入必胜点(N点). //对手走完又只能把N留给我们
取子游戏算法实现
步骤1:将所有终结位置标记为必败点(P点);
步骤2: 将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点)
步骤3:如果从某个点开始的所有一步操作都只能进入必胜点(N点) ,则将该点标记为必败点(P点) ;
步骤4: 如果在步骤3未能找到新的必败(P点),则算法终止;否则,返回到步骤2
/*
上面的说法似乎不太好理解。我简单讲下。显然我们可以从终结条件递推回来。往后往前开始扫描,(终结状态方向为后)
a.如果当前是P点,那么一步(向前)可以走到的都是N点
b.如果当前点未标明P/N属性,那么看看该点向后走是不是都只能到达N点,如果是,那么该点是P点。
c.如果该点是N点,倒无法确定什么。
如果没办法标一个点,那么异常结束。
*/
例题
我是用P N状态递推出来小规模的数据后找规律的,直接递推提交爆内存。(x,y)中,x和y都是奇数的话,那么无论怎么移动,x和y中都有至少有一个偶数,而且再移动一步后,都可以变成xy全奇数。
Nim-Sum
直接说结论好了。(Bouton's Theorem)对于一个Nim游戏的局面(a1,a2,...,an),
它是P-position 当且仅当a1^a2^...^an=0,其中^表示异或(xor)运算。
根据定义,证明一种判断position的性质的方法的正确性,只需证明三个命题:
1、这个判断将所有terminal position 判为P-position;
2、根据这个判断被判为N-position 的局面一定可以移动到某个 P-position;
3、根据这个判断被判为 P-position 的局面无法移动到某个P-position。
第一个命题显然,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'不是一个合法的移动。证毕。
// 简单理解,异或2次可以翻转回来,我们只改一个数字的话,翻不回来。
例题
Being a Good Boy in Spring Festival
求Nim-Sum,非零才能赢。然后每次从Nim-Sum去掉一堆扑克牌,结果是否小于这堆扑克牌数,如果能,方法数+1
ZOJ 3529 A Game Between Alice and Bob
上面强调了一点,游戏中每次拿走的石头数目是不限的,而这道题需要运用技巧进行转换。
求出因子数即可!可以用dp,然后在筛素数的时候顺便记录一下其中一个最大的因子。源码可以看这里:
http://growthinking.com/archives/1777
Sprague-Grundy 函数
现在我们来研究一个看上去似乎更为一般的游戏:给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负。事实上,这个游戏可以认为是所有 Impartial Combinatorial Games 的抽象模型。也就是说,任何一个 ICG都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边来抽象成这个“有向图游戏”。下面我们就在有向无环图的顶点上定义 Sprague-Garundy函数。
mex(minimal excludant)运算
定义表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于一个给定的有向无环图,定义关于图的每个顶点的 Sprague-Garundy 函数 g 如下:
g(x)=mex{ g(y) | y是 x的后继 }。
来看一下 SG 函数的性质。首先,所有的 terminal position 所对应的顶点,也就是没有出边的顶点,其 SG 值为 0,因为它的后继集合是空集。然后对于一个g(x)=0 的顶点 x,它的所后继y都满足 g(y)!=0。对于一个g(x)!=0的顶点,必定存在一个后继 y满足g(y)=0。
以上这三句话表明,顶点 x 所代表的 postion 是 P-position 当且仅当 g(x)=0(跟P-positioin/N-position 的定义的那三句话是完全对应的)。我们通过计算有向无环图的每个顶点,就可以对每种局面找到必胜策略了。
例题
sg函数小试牛刀,哈哈
总结一下
一堆石子的游戏 ---->两堆石子的游戏 ---> Nim游戏 ---> 有向图
P/N position <----> Nim-Sum <----> SG函数 //想起一个词 "同构"
【集训笔记】博弈论相关知识【HDOJ 1850【HDOJ2147的更多相关文章
- linux学习笔记-shell-script相关知识
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一.shell script的执行方法 条件:shell脚本文件必须具备可读可执行权限 1.直接命令执行 (1)使用绝对路径执行 ...
- linux学习笔记-wget相关知识
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! wget是非交互式的网络文件下载工具,这里参考帮助文档,记录下实用参数和使用方法. 一.wget的实用参数: wget: 用法: ...
- linux学习笔记-文件相关知识
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一.文件属性 在当前用户家目录下以ls -al命令输出为例: -rw-r--r-- 1 renren ...
- linux学习笔记-目录相关知识
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! linux的目录结构及作用是根据fhs标准定制的,以下列出一些常用的目录的作用,以及fhs官方网站的连接 FHS官方网站的连接: ...
- 好记性不如烂笔头-linux学习笔记4apache相关知识
apache 启动有2种模式 1是prefork模式,每个进程对应一个线程,如果是比较稳定的平台,那么prefork模式是worker模式 比较好,效率高,但是吃的内存比较大. 2 如果是高负载高并发 ...
- 《Python网络编程》学习笔记--从例子中收获的计算机网络相关知识
从之前笔记的四个程序中(http://www.cnblogs.com/take-fetter/p/8278864.html),我们可以看出分别使用了谷歌地理编码API(对URL表示地理信息查询和如何获 ...
- listener监听器的相关知识
从别人的博客上我学习了listener的相关知识现在分享给大家 1.概念: 监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上 ...
- MySQL学习笔记-事务相关话题
事务机制 事务(Transaction)是数据库区别于文件系统的重要特性之一.事务会把数据库从一种一致状态转换为另一个种一致状态.在数据库提交工作时,可以确保其要么所有修改都已经保存了,要么所有修改都 ...
- 大数据学习笔记——Linux基本知识及指令(理论部分)
Linux学习笔记整理 上一篇博客中,我们详细地整理了如何从0部署一套Linux操作系统,那么这一篇就承接上篇文章,我们仔细地把Linux的一些基础知识以及常用指令(包括一小部分高级命令)做一个梳理, ...
随机推荐
- android--email发送邮件,文本还有附件形式的邮件
1.首先用的jar包为javaemail 下载地址: https://yunpan.cn/cB3kY8WIvcGtU (提取码:e042) 2.工具包 package com.kllayhello.u ...
- C++标准程序库的输入输出流(I/O Stream)复制文件(4种方法)
使用C++标准程序库的输入输出流(I/O Stream)复制文件,存在许多的方法, 方法一:逐个字符复制#include < fstream > std::ifstream ...
- 宣布与 NBC 合作直播索契冬季奥运
奥运开始的那天早些时候,NBC 和 Microsoft 宣布选择 Windows Azure 媒体服务为 2014 年俄罗斯索契冬奥会提供现场直播.与以往不同,以往的冬奥会采用了有限的流,但本届 ...
- docpad建站记录
记一下用docpad建站的过程作为备忘.不定时更新 why docpad wordpress对我来说太过于臃肿,我就想要个代码干净的小站来写东西.想要个markdown为基础的静态站. 比较流行的St ...
- Sicily-1028
一. 题意: 算出汉诺塔移动序列中对应位置的号码,数据规模很大,所以不能单纯递归,而是要找出汉诺塔序列的规律. 二. 汉诺塔数列 为了得出最少的移动步数,当n为偶数时,最上 ...
- HTTP协议是无状态协议,怎么理解?
Http是一个无状态协议,同一个会话的连续两个请求互相不了解,他们由最新实例化的环境进行解析,除了应用本身可能已经存储在全局对象中的全部信息外,该环境不保存与会话有关的不论什么信息. 自己的理解,在 ...
- asp.net MVC 使用JQuery.Ajax
使用到:Jquery.js 以及 Newtonsoft.Json.dll 客户端调用方式: $("#ButAjax").click(function() {$.ajax({type ...
- Java多线程之synchronized(一)
在上节中已经说过了“非线程安全”是如何出现的,链接如下:http://www.cnblogs.com/chentong/p/5650137.html,那么怎么解决“非线程安全”问题呢,只需要在两个线程 ...
- 字符串分割函数Demo
#include <stdio.h> int getLength(char *string); int main(int argc, char **argv){ char str[12] ...
- Dojo实现Tabs页报错(三)
用Dojo实现tab页的过程中,没有引用“on.js”,但是firebug调试时一直提示如下错误: on.js源码如下: define(["./has!dom-addeventlistene ...