博弈习题

hdu1846

1、  本游戏是一个二人游戏;
2、  有一堆石子一共有n个;
3、  两人轮流进行;
4、  每走一步可以取走1…m个石子;
5、  最先取光石子的一方为胜;
如果游戏的双方使用的都是最优策略,请输出哪个人能赢。

Input
输入数据首先包含一个正整数C(C<=100),表示有C组测试数据。
每组测试数据占一行,包含两个整数n和m(1<=n,m<=1000),n和m的含义见题目描述。
Output
如果先走的人能赢,请输出“first”,否则请输出“second”,每个实例的输出占一行。
 
典型的巴什博奕,要讨论n和m的关系,显然,如果有小于m个,那先取者获胜;如果有m+1个,后取者总能获胜。所以某玩家只要当前形势是m+1个,或者m+1个的倍数,那么他最后肯定能获胜的啦~~即,n = (m+1)*r + s,那么取s个就好啦~~~
 
#include<iostream>
#include<cstdio>

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

 

hdu1847

大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此。当然,作为在考场浸润了十几载的当代大学生,Kiki和Cici更懂得考前的放松,所谓“张弛有道”就是这个意思。这不,Kiki和Cici在每天晚上休息之前都要玩一会儿扑克牌以放松神经。
“升级”?“双扣”?“红五”?还是“斗地主”?
当然都不是!那多俗啊~
作为计算机学院的学生,Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、  总共n张牌;
2、  双方轮流抓牌;
3、  每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、  抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?
当然,打牌无论谁赢都问题不大,重要的是马上到来的CET-4能有好的状态。

Input

输入数据包含多个测用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。
 Output

如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<set>
using namespace std;
int mex(set<int>&s){
    int a = 0;
    if(s.count(a))
        a++;
    return a;
}
int main(){
    int i;
    int d[15];
    int numd = 10;
    for(int i = 0;i < 10;i++){
        d[i] = pow(2,i);
    }
    int n;
    while(scanf("%d",&n) != EOF){
        int sg[1010] = {0};
        for(int i = 1;i <= n;i++){
            set<int>s; 
            for(int j = 0;j < numd;j++){
                if(i - d[j] < 0)
                    continue;
                s.insert(sg[i-d[j]]);
            }
            sg[i] = mex(s);
        }
        if(sg[n] == 0){
            cout << "Cici" << endl;
        }
        else
         cout <<"Kiki" << endl; 
        
    }
    
} 
 
hdu1848
任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:
F(1)=1;
F(2)=2;
F(n)=F(n-1)+F(n-2)(n>=3);
所以,1,2,3,5,8,13……就是菲波那契数列。
在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。
今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下:
1、  这是一个二人游戏;
2、  一共有3堆石子,数量分别是m, n, p个;
3、  两人轮流走;
4、  每走一步可以选择任意一堆石子,然后取走f个;
5、  f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);
6、  最先取光所有石子的人为胜者;

假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。奇异态先手必输!
 
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<cstring>

using namespace std;

const int N = 1010;
int sg[N];
bool Hash[N];

void sg_solve(int *s,int t){   ///N求解范围 S[]数组是可以每次取的值,t是s的长度。下标从0开始 
    int i,j;
    memset(sg,0,sizeof(sg));
    for(i=1; i<N; i++){
        memset(Hash,0,sizeof(Hash));
        for(j=0; j<t; j++)
            if(i - s[j] >= 0)
                Hash[sg[i-s[j]]] = 1;
        for(j=0; j<N; j++)
            if(!Hash[j])
                break;
        sg[i] = j;
    }
}

int main(){
    int n,m,p,res;
    int s[100] = {1,2};
    int nums = 2;
    while(1){
        s[nums] = s[nums-1] + s[nums-2];
        if(s[nums] > 1000)
            break;
        nums++;
    }
    sg_solve(s,nums);
    while(scanf("%d%d%d",&n,&m,&p) != EOF){
        if(n + m + p == 0)
            break;
        res = sg[n]^sg[m]^sg[p];
        if(res == 0) printf("Nacci\n");
        else printf("Fibo\n");        
    }
    return 0;
}

 

hdu1849

Rabbit and Grass(未变形的裸nim博弈)

大学时光是浪漫的,女生是浪漫的,圣诞更是浪漫的,但是Rabbit和Grass这两个大学女生在今年的圣诞节却表现得一点都不浪漫:不去逛商场,不去逛公园,不去和AC男约会,两个人竟然猫在寝食下棋……
说是下棋,其实只是一个简单的小游戏而已,游戏的规则是这样的:
1、棋盘包含1*n个方格,方格从左到右分别编号为0,1,2,…,n-1;
2、m个棋子放在棋盘的方格上,方格可以为空,也可以放多于一个的棋子;
3、双方轮流走棋;
4、每一步可以选择任意一个棋子向左移动到任意的位置(可以多个棋子位于同一个方格),当然,任何棋子不能超出棋盘边界;
5、如果所有的棋子都位于最左边(即编号为0的位置),则游戏结束,并且规定最后走棋的一方为胜者。

对于本题,你不需要考虑n的大小(我们可以假设在初始状态,棋子总是位于棋盘的适当位置)。下面的示意图即为一个1*15的棋盘,共有6个棋子,其中,编号8的位置有两个棋子。



大家知道,虽然偶尔不够浪漫,但是Rabbit和Grass都是冰雪聪明的女生,如果每次都是Rabbit先走棋,请输出最后的结果。
Input
输入数据包含多组测试用例,每个测试用例占二行,首先一行包含一个整数m(0<=m<=1000),表示本测试用例的棋子数目,紧跟着的一行包含m个整数Ki(i=1…m; 0<=Ki<=1000),分别表示m个棋子初始的位置,m=0则结束输入。
Output

如果Rabbit能赢的话,请输出“Rabbit Win!”,否则请输出“Grass Win!”,每个实例的输出占一行。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<cstring>

using namespace std;

int a[1010];

int main(){
    int n,res;
    while(scanf("%d",&n)){
        res = 0;
        if(n == 0)
            break;
        for(int i = 0;i < n;i++){
            scanf("%d",&a[i]);
            res = res^a[i];
        }
        if(res == 0)
            printf("Grass Win!\n");
        else
            printf("Rabbit Win!\n");
    }
    return 0;
}

 

hdu1850

Being a Good Boy in Spring Festival

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8434    Accepted Submission(s): 5172


Problem Description

一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧

陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐

如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~

下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”

Input
输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1<M<=100),表示扑克牌的堆数,紧接着一行包含M个整数Ni(1<=Ni<=1000000,i=1…M),分别表示M堆扑克的数量。M为0则表示输入数据的结束。
Output
如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。
 
(这道题比较别出心裁,首先判断,是否是奇异态,如果是奇异态,输出0,否则,看能否去除一定是自构成奇异态,无非是让这一堆得数量 等于其他堆的数量异或值,那么求其他堆异或总值,不用再求一次,只要用总异或对当前值再进行一次异或就可以了)
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<cstring>

using namespace std;

int main(){
    int n,sum,res;
    int a[110];
    while(scanf("%d",&n) != EOF){
        if(n == 0)
            break;
        sum = 0;
        for(int i = 0;i < n;i++){
            scanf("%d",&a[i]);
            sum = sum^a[i];
        }
        if(sum == 0){
            printf("0\n");
        }
        else{
            res = 0;
            for(int i = 0;i < n;i++){
                if((sum^a[i]) <= a[i])
                    res++;
            }
            printf("%d\n",res);
        }
        
    }
    return 0;
}

 

 

hdu1536 典型nim博弈加sg函数!!!典型哦

S-Nim

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8918    Accepted Submission(s): 3726


Problem Description
Arthur and his sister Caroll have been playing a game called Nim for some time now. Nim is played as follows:


  The starting position has a number of heaps, all containing some, not necessarily equal, number of beads.

  The players take turns chosing a heap and removing a positive number of beads from it.

  The first player not able to make a move, loses.


Arthur and Caroll really enjoyed playing this simple game until they recently learned an easy way to always be able to find the best move:


  Xor the number of beads in the heaps in the current position (i.e. if we have 2, 4 and 7 the xor-sum will be 1 as 2 xor 4 xor 7 = 1).

  If the xor-sum is 0, too bad, you will lose.

  Otherwise, move such that the xor-sum becomes 0. This is always possible.


It is quite easy to convince oneself that this works. Consider these facts:

  The player that takes the last bead wins.

  After the winning player's last move the xor-sum will be 0.

  The xor-sum will change after every move.


Which means that if you make sure that the xor-sum always is 0 when you have made your move, your opponent will never be able to win, and, thus, you will win.

Understandibly it is no fun to play a game when both players know how to play perfectly (ignorance is bliss). Fourtunately, Arthur and Caroll soon came up with a similar game, S-Nim, that seemed to solve this problem. Each player is now only allowed to remove a number of beads in some predefined set S, e.g. if we have S =(2, 5) each player is only allowed to remove 2 or 5 beads. Now it is not always possible to make the xor-sum 0 and, thus, the strategy above is useless. Or is it?

your job is to write a program that determines if a position of S-Nim is a losing or a winning position. A position is a winning position if there is at least one move to a losing position. A position is a losing position if there are no moves to a losing position. This means, as expected, that a position with no legal moves is a losing position.
 

 

Input
Input consists of a number of test cases. For each test case: The first line contains a number k (0 < k ≤ 100 describing the size of S, followed by k numbers si (0 < si ≤ 10000) describing S. The second line contains a number m (0 < m ≤ 100) describing the number of positions to evaluate. The next m lines each contain a number l (0 < l ≤ 100) describing the number of heaps and l numbers hi (0 ≤ hi ≤ 10000) describing the number of beads in the heaps. The last test case is followed by a 0 on a line of its own.
 

 

Output
For each position: If the described position is a winning position print a 'W'.If the described position is a losing position print an 'L'. Print a newline after each test case.
 

 

Sample Input
2 2 5 3 2 5 12 3 2 4 7 4 2 3 7 12 5 1 2 3 4 5 3 2 5 12 3 2 4 7 4 2 3 7 12 0
 

 

Sample Output
LWW WWL
 
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<cstring>

using namespace std;

const int N = 10005;//每堆个数的最大值 
int sg[N],a[105];
bool Hash[N];

void sg_solve(int *s,int t){   ///N求解范围 S[]数组是可以每次取的值,t是s的长度。下标从0开始 
    int i,j;
    memset(sg,0,sizeof(sg));
    for(i=1; i<N; i++){
        memset(Hash,0,sizeof(Hash));
        for(j=0; j<t; j++)
            if(i - s[j] >= 0)
                Hash[sg[i-s[j]]] = 1;
        for(j=0; j<N; j++)
            if(!Hash[j])
                break;
        sg[i] = j;
    }
}

int main(){
    int n,n2,n3,res,cur;
    int s[110];
    while(scanf("%d",&n)){
        if(n == 0)
            break;
        for(int i = 0;i < n;i++){
            scanf("%d",&s[i]);
        }
        sg_solve(s,n);
        scanf("%d",&n2);
        while(n2--){
            scanf("%d",&n3);
            res = 0;
            while(n3--){
                scanf("%d",&cur);
                res = res^sg[cur];
            }
            if(res == 0)printf("L");
            else printf("W");
        }
        printf("\n");
    }
    return 0;
}

hdu1907 变形的!裸nim博弈,变成取完输态(判断是否为充裕堆)

John

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 5617    Accepted Submission(s): 3252


Problem Description
Little John is playing very funny game with his younger brother. There is one big box filled with M&Ms of different colors. At first John has to eat several M&Ms of the same color. Then his opponent has to make a turn. And so on. Please note that each player has to eat at least one M&M during his turn. If John (or his brother) will eat the last M&M from the box he will be considered as a looser and he will have to buy a new candy box.

Both of players are using optimal game strategy. John starts first always. You will be given information about M&Ms and your task is to determine a winner of such a beautiful game.

 

 

Input
The first line of input will contain a single integer T – the number of test cases. Next T pairs of lines will describe tests in a following format. The first line of each test will contain an integer N – the amount of different M&M colors in a box. Next line will contain N integers Ai, separated by spaces – amount of M&Ms of i-th color.

Constraints:
1 <= T <= 474,
1 <= N <= 47,
1 <= Ai <= 4747

 

 

Output
Output T lines each of them containing information about game winner. Print “John” if John will win the game or “Brother” in other case.

 

 

Sample Input
2 3 3 5 1 1 1
 

 

Sample Output
John Brother
#include<iostream>
#include<cstdio>
using namespace std;
//异或结果为0,先手必输
//如果是变形的话 

int a[50];
int n;
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        int res = 0;
        int book  = 0;
        for(int i = 0;i < n;i++){
            scanf("%d",&a[i]);
            res = res^a[i];
            if(a[i] > 1) book++; 
        }
        
        if((res == 0&&book)||(res == 1&&book == 0))///偶数堆 每堆1个  和  奇异态(先手输 
            cout << "Brother" << endl;
        else
            cout << "John" << endl; 
    }
    
    
    return 0;
} 

 

 

 

 

转载于:https://www.cnblogs.com/xuyanqd/p/8990130.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值