基础博弈论(hdu1527+hdu2188+poj1740)

一、基础概念

1、奇异局势:指你一定会输的状态,又叫必败状态。

例如威佐夫博弈中的(0,0)(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)、...... 、(ak,bk) (k = 0, 1, 2, 3,......)。

自己走路正确一定能赢得:(0,0),(1,k),(k,2),(1+k,2+k)

2、奇异局势的性质

 (1):每个自然数都包含在且只包含在一个奇异局势中。

 (2):对任意的奇异局势,任何合法的操作都会使其成为非奇异局势,也就是奇异局势的所有后继状态均为非奇异局势。

 (3):任何非奇异局势都可以通过某种合法操作得到奇异局势,即奇异局势的所有后继状态中存在奇异局势。

若符合奇异局势,则先手输。否则先手胜。

二、威佐夫博弈(hdu1527)

对于(ak,bk),ak是未在前面出现过的最小自然数(不包括k的),bk=ak+k;

然后就得到了奇异局势的状态公式:

ak = [ k * (1 + √5) / 2 ] , ([x]表示对x取整,也就是 (int)x ) 

        bk = ak + k 

对于hdu1527这个取石子这个题目,也就有了更好的方法。

首先给出的两个数a,b一定要符合上面的公式。

b-a=a+k-a=k;

所以a=[|b-a|*(1 + √5) / 2]

第一步先求出|b-a|=k

第二部k* (1 + √5) / 2 =a0;

第三部比较a0与a是否相等。若相等, 则为必败状态,返回0;不相等, 返回1;


代码如下:(sqrt函数里面写5会显示编译错误,写5.0就对了,可能是函数重载的问题)

#include <stdio.h>
#include<math.h>
#include<stdlib.h>
int main()
{
    int a,b;
    while(scanf("%d %d",&a,&b)!=EOF)
    {
        int k=abs(a-b);
        double p=(1+sqrt(5.0))/2;
        int a0=int(p*k);
        if(a>b) a=b;
        if(a==a0) printf("0\n");
        else printf("1\n");
    }
    return 0;
}


二、巴什博奕(hdu2188)

对于四川同胞遭受的灾难,全国人民纷纷伸出援助之手,几乎每个省市都派出了大量的救援人员,这其中包括抢险救灾的武警部队,治疗和防疫的医护人员,以及进行心理疏导的心理学专家。根据要求,我校也有一个奔赴灾区救灾的名额,由于广大师生报名踊跃,学校不得不进行选拔来决定最后的人选。经过多轮的考核,形势逐渐明朗,最后的名额将在“林队”和“徐队”之间产生。但是很巧合,2个人的简历几乎一模一样,这让主持选拔的8600很是为难。无奈,他决定通过捐款来决定两人谁能入选。 
选拔规则如下: 
1、最初的捐款箱是空的; 
2、两人轮流捐款,每次捐款额必须为正整数,并且每人每次捐款最多不超过m元(1<=m<=10)。 
3、最先使得总捐款额达到或者超过n元(0<n<10000)的一方为胜者,则其可以亲赴灾区服务。 
我们知道,两人都很想入选志愿者名单,并且都是非常聪明的人,假设林队先捐,请你判断谁能入选最后的名单? 
Input
输入数据首先包含一个正整数C,表示包含C组测试用例,然后是C行数据,每行包含两个正整数n,m,n和m的含义参见上面提到的规则。
Output
对于每组测试数据,如果林队能入选,请输出字符串"Grass", 如果徐队能入选,请输出字符串"Rabbit",每个实例的输出占一行。
Sample Input
2
8 10
11 10
Sample Output
Grass
Rabbit

对于这道题我开始的想法其实是分情况讨论,分了三种情况:

  1、若n<m,则先手获胜

  2、若n-m==1,则后手获胜

  3、若n-m>=1,则先手获胜。

开始的思路是没问题的,在n-m~m这个状态内,谁先手谁就获胜。所以是先到n-m(包括n-m),谁就输,因为下一个人一定可以加到m。

后来提交之后显示WA,百度了下巴什博奕,发现第三条情况下,如果n=22,m=10的时候,就不符合条件。

后来发现应该是n%(m+1)==0的时候先手赢,因为先手要想赢,必须最后一个自己来拿(对手足够聪明)。

如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s

个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取

法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。



#include <stdio.h>
int main()
{
    int x;
    int m,n;
    scanf("%d",&x);
    while(x--)
    {
        scanf("%d %d",&n,&m);
        if(n%(m+1)==0)printf("Rabbit\n");
        else printf("Grass\n");
    }
    return 0;
}

三、找规律博弈(poj1740)

Alice and Bob decide to play a new stone game.At the beginning of the game they pick n(1<=n<=10) piles of stones in a line. Alice and Bob move the stones in turn. 
At each step of the game,the player choose a pile,remove at least one stones,then freely move stones from this pile to any other pile that still has stones. 
For example:n=4 and the piles have (3,1,4,2) stones.If the player chose the first pile and remove one.Then it can reach the follow states. 
2 1 4 2 
1 2 4 2(move one stone to Pile 2) 
1 1 5 2(move one stone to Pile 3) 
1 1 4 3(move one stone to Pile 4) 
0 2 5 2(move one stone to Pile 2 and another one to Pile 3) 
0 2 4 3(move one stone to Pile 2 and another one to Pile 4) 
0 1 5 3(move one stone to Pile 3 and another one to Pile 4) 
0 3 4 2(move two stones to Pile 2) 
0 1 6 2(move two stones to Pile 3) 
0 1 4 4(move two stones to Pile 4) 
Alice always moves first. Suppose that both Alice and Bob do their best in the game.
You are to write a program to determine who will finally win the game. 
Input
The input contains several test cases. The first line of each test case contains an integer number n, denoting the number of piles. The following n integers describe the number of stones in each pile at the beginning of the game, you may assume the number of stones in each pile will not exceed 100. 
The last test case is followed by one zero. 
Output
For each test case, if Alice win the game,output 1,otherwise output 0. 
Sample Input
3
2 1 3
2
1 1
0
Sample Output
1
0

解题思路(摘自网上):

 1、先考虑1堆的时候,1堆当然是N点(必胜点)。

 2、然后考虑2堆,细想一下可以发现,当2堆一样时,这个时候的目的就是要把对方给逼到只有2堆都是1的时候,就能必胜了。但是想一下,后手只要模仿先手所做的动作,那么最后就会形成两堆都是1的局势,所以当2堆相同时,是一个P点(必败点)。注意当2堆不一样的时候,先手可以把它变成一样,此时变为N点。

 3、考虑3堆,这个时候,先手必定是可以把局势变成2堆相同的堆的,那么先手肯定胜利,为N点。(发现,当堆为偶数堆两两同高的时候,此时是P点)

 4、当n >= 4堆的时候可以发现,可以把堆的高度按从小到大排列。

   当n为偶数的时候,可以把最高的那一堆跟最小的那一堆变成一样,然后把高度差用来平衡剩余的那些堆,注意一定是可以平衡的,因为把剩余的堆相邻两两的差值投射到y轴上发现这些离散的线段和小于最高堆于最小堆的差值。

   当n为奇数的时候,可以把最高堆给去掉,然后分配给其它堆,注意前面的相邻堆两两的差值投射到y轴,最后的总和还是小于最高堆的。


#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 15

int n;
int a[N];

int main()
{
    while (~scanf("%d",&n))
    {
        if (!n) break;
        for (int i=1;i<=n;++i) scanf("%d",&a[i]);
        printf("%d %d\n",n,n%2);
        if (n%2) {puts("1");continue;}
        sort(a+1,a+n+1);
        bool flag=true;
        for (int i=2;i<=n;i+=2)
            if (a[i]!=a[i-1]) {flag=false;break;}
        if (flag) puts("0");
        else puts("1");
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值