暑假训练专题一 博弈
一:巴什博弈:
acm 例题 北京师范大学oj:http://oj.51isoft.com/contest/problem_show.php?pid=23384
post code:
#include<stdio.h> int main() { int n,num,m; scanf("%d",&n); while(n--) { scanf("%d %d",&num,&m); if(num<=m)printf("player1 wins\n"); else { if(num%(m+1)==0)printf("player2 wins\n"); else printf("player1 wins\n"); } } }
n只有一堆n 个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m 个。最后取光者得胜。
n给你n,如何判断先手还是后手赢
n显然如果n<=m,先取者胜。
n显然,如果n=m+1,那么由于一次最多只能取m 个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜
因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r 为任意自然数,s≤m),那么先取者要拿走s 个物品,如果后取者拿走k(k≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)*(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜
二:威佐夫博弈
n有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
必死局的判定:
如果 a遇到了6,10 ,须变化一次,只能对角线,竖着,横着变化。
b仅一次就可以将其变化成为 图中的死局。
因为 在(6,10)之前含有 (1,2)(3,5)(4,7) 横纵都包含了 1-5,向上和往对角线方向都被封死了,都可以转化到这三个数。
只能横着走:
但会被(1,1)(1,2)(3,5)(4,7)对角线封锁,
例如 a遇到(6,10) 竖向变成(3,10),因为包含数字3,则可以转化为(3,5);a将又一次面临死局。
横向变成(6,8),在(3,5)的对角线上,所以可以直接的转化成(3,5);
横向变成(6,6),a直接两个都减去6,致使b输掉了。
必死局的判定:死局的前几项是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10(8,13)、(9,15)、(11,18)、
(12,20)
n我们总结了如下公式:ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,...,n 方括号表示取整函数)
即成立;
威佐夫博弈 博弈分为 必死局和非必死局
必死局的判定:
n任何自然数都包含在一个且仅有一个在必死局势中。
n2、任意操作都可将必死局变为非必死局势。
n3、采用适当的方法,可以将非必死局势变为必死局势。
三:尼姆博弈
求出
n尼姆博奕(Nimm Game):
n对于任何局势(a1, a2,..,..,..,an)
na1(+)a2(+)…(+)an . ( (+)为 按位与)结果为0则先手输,否则先手赢
n另一种题目:
n给出n堆石头的数目,若先手赢问第一步可行的方案数是多少?
n以(7,8,9)为例:
n7---- 0111
n8---- 1000
n9---- 1001
n ————
n 0110(6)结果不为0,先手赢
n第一步方案数为1.
即看^(所有元素异或后的结果)。
中的第一次出现“1”的位数
例如上式,的出的结果为6,出现“1”的次数是第三位,则看7,8,9,中第三位存在几个‘1’,就是最后的方案数。上述只有7,有‘1’,所以方案数为1.
要注意的是,这样判断的结果是 根据 8,9的性质来决定的。
1:判断 取出的个数是 8^9=(7^8^9)^7
使得7变成 (8^9)这样是三个数异或的结果为0;
2: 注意的是 异或后的结果 必须小于 7,这样才成立;
下面是关于异或的性质:
1. a ^ b = b ^ a
2. a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;
3. d = a ^ b ^ c 可以推出 a = d ^ b ^ c.
4. a ^ b ^ a = b.
下面推荐一道杭电上的题: 加深理解:http://acm.hdu.edu.cn/showproblem.php?pid=2176
post code:
#include<stdio.h> int main() { int n,i,a[200010],num,j; while(scanf("%d",&n)&&n!=0) { i=1; scanf("%d",&num); a[i]=num; for(i=2;i<=n;i++) { scanf("%d",&a[i]); num=num^a[i]; } if(num==0)printf("No\n"); else { printf("Yes\n"); for(i=1;i<=n;i++) { if(a[i]>(a[i]^num)) printf("%d %d\n",a[i],a[i]^num); } } } }
算法描述:判断他们的异或是否为0;
为0则成立;
不为0 输出你要取出的值,即每个元素遍历一遍,找出 其他值异或小于他的结果,然后进行输出:这样就得出了正确答案。
自己想了半天:弄懂这道题的解法,挺有意思的:
大家可以参考一下:
这道题:主要是按一下方式进行描述:
如果(b%a,a)是一个必胜态,那就要看b/a的值,如果b/a>1,那么先手可以由状态(a,b)到达(a,b%a+a),这样后手就只能到达(b%a,a),所以此时(a,b)仍然是一个必胜态;如果b/a=1,那么先手只能到达(b %a,a),因此(a,b)就是一个必败态.
上面这句话是关键: 在不断得寻找找必败态和必胜态, 将其赋值给给出的初始数据,最后的到自己想要的结果。
上面这句话是关键: 在不断得寻找找必败态和必胜态, 将其赋值给给出的初始数据,最后的到自己想要的结果。
post code:
#include<stdio.h> void swap(int &a,int &b); int fun(int a,int b); int main() { int a,b,result; while(scanf("%d %d",&a,&b)&&(a!=0||b!=0)) { if(a<b)swap(a,b); result=fun(a,b); //寻找输入数据的状态。 if(result==1)printf("Stan wins\n"); else printf("Ollie wins\n"); } } void swap(int &a,int &b) { int temp; temp=a; a=b; b=temp; } int fun(int a,int b) { if(a%b==0)return 1; else {if( fun(b,a%b)&&(a/b==1) ) 对于其中的定量描述;就如同上面所描述的。 return 0 ; else return 1; } }