码迷,mamicode.com
首页 > 其他好文 > 详细

博弈学习 1

时间:2016-07-22 23:02:02      阅读:232      评论:0      收藏:0      [点我收藏+]

标签:


看完三位博主的文章再做了几个简单题后的简单总结。
可以看原文,比较详细~~

链接 1: http://blog.csdn.net/logic_nut/article/details/4711489
链接 2: http://blog.sina.com.cn/s/blog_83d1d5c70100y9yd.html
链接 3: http://blog.csdn.net/luomingjun12315/article/details/45479073

 

几个定义与结论:

1)两个状态
P-Position : P 指的是 Previous ,上一次移动的人有必胜策略,即先手必败
N-Position :N 指的是 Next ,这次移动的人有必胜策略,即先手必胜

1. 无法进行任何移动的局面(也就是terminal position)是P-position;
2. 可以移动到P-position的局面是N-position。 也就是说如果一个状态是 P-Position那么能够转移到这个局面的所有局面都是 N-Position
3. 所有移动都导致N-position 的局面是P-position。也就是说它之前的状态中有一个 N-position,就可以确定这个局面是 P-position

这个可以结合链接 1 原文想一想,就不糊涂辣 ~


组合游戏:

1、有且仅有两个玩家
2、两名玩家交替在 有限的移动集合(比如:取石子游戏,石子是有限的,棋盘中的棋盘大小的有限的)中对游戏进行合法的移动(不能不移动)
3、如果轮到某名选手移动,且这个局面的合法的移动集合为空(也就是说此时无法进行移动),则这名选手负。

Nim游戏:
它是组合游戏(Combinatorial Games)的一种,准确来说,属于“Impartial Combinatorial Games”(以下简称ICG)。

通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,
合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。

一个结论:
(Bouton‘s Theorem)对于一个Nim游戏的局面(a1,a2,...,an),它是P-position当且仅当a1^a2^...^an=0,其中^表示异或(xor)运算。

证明在链接 1 中~


几个题都是简单的巴什博奕。

巴什博奕(Bash Game):有一堆n个物品,两人轮流从堆中取物品,每次取 x 个 ( 1 ≤ x ≤ m)。最后取光者为胜。


HDU 1846

1) 可以用上述的 P-Position 和 N-Position的定义解,多模拟几次就可以发现规律,然后就可愉快解题了。

例如 n m 分别为 6 2时
即石子有 6 颗,两人轮流取,每次可以可取 1~2 颗 。

那么所有的状态应该是这样的:

有x颗时  0 1 2 3 4 5 6
Position P N N P N N P

发现规律了咩~


2) 也可以这样想 ,复制一段来自链接 3 的推导:

如果 n = m + 1, 一次至多取 m 个,所以无论先取者,取了多少个,一定还剩余 x 个( 1 ≤ x ≤ m)。所以,后取者必胜。
因此我们发现了取胜的秘诀:如果我们把 n 表示为 n = (m + 1) * r + s 。(0 ≤ s < m , r ≥ 0)。
先取者 拿走 s 个, 后取者 拿走 k 个 (1 ≤ k ≤ m),那么先取者 再 拿走 m + 1 - k 个。
结果还剩下 ( m + 1 ) * ( r - 1 ) 个。我们只要始终给对手留下 m + 1 的倍数,那么 先取者 肯定必胜。
现在 我们可以知道,如果 s = 0,那么后取者必胜。 否则 先取者 必胜。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        if(n%(m+1) == 0) printf("second\n");
        else printf("first\n");
    }
    return 0;
}

 


HDU2147

题意: N*M棋盘,最初棋子在右上角,即(1,M) 每次可以往左、下、左下走一步,最后走到左下角的胜 。

最先开始 把它看做一个类似取石子的游戏,每次可走1~2步的(走到左下算两步= =)

然后 WA 掉了 WA 掉了 掉了 了。。

正解是又照着定义模拟了一遍 ,找出规律,即当 N M 都是奇数的时候先手必败。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0) break;
        if((n&1) && (m&1)) printf("What a pity!\n");
        else printf("Wonderful!\n");
    }
    return 0;
}

 


HDU2188
与 HDU 1846 同理哟

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
int main()
{
    int t,n,m;
    cin>>t;
    while(t--){
        cin>>n>>m;
        if(n%(m+1)) cout<<"Grass"<<endl;
        else cout<<"Rabbit"<<endl;
    }
    return 0;
}

 


HDU 2149

按照上面的推导 ,如果我们把 n 表示为 n = (m + 1) * r + s 。(0 ≤ s < m , r ≥ 0)。
那么 s 就是第一次的加价 。如果加价大于本价,也就是说先手可以直接买到了 ,那么出的价就可以是 m~n这一段里的任一。
最先开始自己没想清楚,以为 1~s 都可以,= = 后来想了下,如果不直接加到 S ,那不是给了对方可乘之机 = =

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
int main()
{
    int t,n,m;
    while(scanf("%d%d",&m,&n)!=EOF){
        if(m%(n+1)){
            int x = m%(n+1);            
            if(n>=m){
                cout<<m;
                for(int i=m+1;i<=n;i++) printf(" %d",i);
            } 
            else printf("%d",x);                        
        }
        else printf("none");    
        cout<<endl;    
    }
    return 0;
}

 

*/

 

博弈学习 1

标签:

原文地址:http://www.cnblogs.com/ember/p/5697197.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!