Bash游戏V1

有一堆石子共同拥有N个。

A B两个人轮流拿。A先拿。每次最少拿1颗。最多拿K颗。拿到最后1颗石子的人获胜。如果A B都很聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。

比如N = 3。K = 2。不管A怎样拿,B都能够拿到最后1颗石子。
Input
第1行:一个数T。表示后面用作输入測试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行2个数N,K。中间用空格分隔。(1 <= N,K <= 10^9)
Output
共T行。假设A获胜输出A,假设B获胜输出B。
Input演示样例
4
3 2
4 2
7 3
8 3
Output演示样例
B
A
A
B

解题思路:
    假设是(k+1)的整数倍。先手取不论什么一个1~k内的数x。后手都能够取(k+1-x)个石子,于是k+1的整数倍是先手的必败态,同理,不是k+1的整数倍时,先手能够取n%(k+1)个石子。从而使后手必败。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; int main()
{
int n,k,t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
if(n%(k+1))
printf("A\n");
else
printf("B\n");
}
return 0;
}

有一堆石子共同拥有N个。A B两个人轮流拿,A先拿。每次仅仅能拿1,3,4颗。拿到最后1颗石子的人获胜。如果A B都很聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。
比如N = 2。

A仅仅能拿1颗,所以B能够拿到最后1颗石子。

Input
第1行:一个数T,表示后面用作输入測试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行1个数N。(1 <= N <= 10^9)
Output
共T行,假设A获胜输出A。假设B获胜输出B。
Input演示样例
3
2
3
4
Output演示样例
B
A
A


解题思路:
    这题假设直接想不出来能够用sg函数打表发现规律来做。
 sg函数和sg定理:
     对于随意状态x。定义SG(x)=mex(S),当中S是x的后继状态的SG函数的集合。mex(s)表示不在S内的最小负整数。比方,x有6个后继状态。SG函数数值分别为0,1,1,2,4,7,则SG(x)=3,由于3是第一个没有出如今后继状态SG函数值集合中的非负整数。

这题的sg函数代码:
const int maxn=45;
bool vis[maxn];
int sg[maxn];
int a[5]={1,3,4};
void sgs()
{
for(int i=0;i<maxn;i++)
{
memset(vis,false,sizeof(vis));
for(int j=0;j<3;j++)
{
if(i>=a[j])
vis[sg[i-a[j]]]=true;
}
for(int j=0;;j++)
{
if(!vis[j])
{
sg[i]=j;
break;
}
}
printf("%d %d\n",i,sg[i]);
}
}

通过打表发现,7的整数倍和n%7==2的是必败态。


证明:
    1,对于2。肯定必败。
    2,对于1,3,4,先手必胜。

    3,对于5,6,先手能够取3,4,让后手进入必败态2。
    2,对于7。不管先手取什么。后手都能够让其进入必败态。先手取1,后手取4,进入必败态2;先        手取3,后手取4;先手取4,后手取3就可以。于是仅仅要是7的倍数或%7余2的。都是必败态,其它      都为必胜态。

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std; int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
if(n%7==0||n%7==2)
printf("B\n");
else
printf("A\n");
}
return 0;
}

有一堆石子共同拥有N个。A B两个人轮流拿,A先拿。每次拿的数量仅仅能是2的正整数次幂。比方(1,2,4,8,16....)。拿到最后1颗石子的人获胜。

如果A B都很聪明,拿石子的过程中不会出现失误。给出N。问最后谁能赢得比赛。

比如N = 3。

A仅仅能拿1颗或2颗,所以B能够拿到最后1颗石子。(输入的N可能为大数)

Input
第1行:一个数T,表示后面用作输入測试的数的数量。

(1 <= T <= 1000)
第2 - T + 1行:每行1个数N。 (1 <= N <= 10^1000)
Output
共T行。假设A获胜输出A,假设B获胜输出B。
Input演示样例
3
2
3
4
Output演示样例
A
B
A


解题思路:
   sg函数:
const int maxn=1000+100;
int sg[maxn];
bool vis[maxn];
int main()
{
for(int i=0;i<50;i++)
{
memset(vis,false,sizeof(vis));
for(int j=0;(1<<j)<=i;j++)
{
int s=i-(1<<j);
vis[sg[s]]=true;
}
for(int j=0;;j++)
{
if(!vis[j])
{
sg[i]=j;
break;
}
}
printf("%d ",sg[i]);
}
return 0;
}

发现仅仅要是3的整数倍就能够。



证明:
   随意1个3的整数倍都能够转化为2*n个2的正整数幂的和。通过枚举能够发现:
   (2^0)%3=1; (2^1)%3=2;(2^2)%3=1;(2^3)%3=2;
   总是1和2,随意1个%3为1的加上%3为2的就能够组成%为3的数了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
char s[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
int n=strlen(s);
int ans=0;
for(int i=0;i<n;i++)
{
ans=ans+(s[i]-'0');
}
if(ans%3)
printf("A\n");
else
printf("B\n");
}
return 0;
}

Bash游戏V4(斐波那契博弈)


有一堆石子共同拥有N个。A B两个人轮流拿,A先拿。每次拿的数量最少1个,最多不超过对手上一次拿的数量的2倍(A第1次拿时要求不能全拿走)。拿到最后1颗石子的人获胜。如果A B都很聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。
比如N = 3。

A仅仅能拿1颗或2颗,所以B能够拿到最后1颗石子。

Input
第1行:一个数T,表示后面用作输入測试的数的数量。

(1 <= T <= 1000)
第2 - T + 1行:每行1个数N。(1 <= N <= 10^9)
Output
共T行,假设A获胜输出A,假设B获胜输出B。
Input演示样例
3
2
3
4
Output演示样例
B
B
A

解题思路:

这个游戏叫做Fibonacci Nim,肯定和Fibonacci数列:f[n]:1,2,3,5,8,13,21,34,55,89,… 有密切的关系。假设试验一番之后,能够推測:先手胜当且仅当n不是Fibonacci数。

换句话说。必败态构成Fibonacci数列。

这里须要借助“Zeckendorf定理”(齐肯多夫定理):不论什么正整数能够表示为若干个不连续的Fibonacci数之和。

先看看FIB数列的必败证明:

1、当i=2时。先手仅仅能取1颗,显然必败,结论成立。

2、如果当i<=k时。结论成立。

则当i=k+1时,f[i] = f[k]+f[k-1]。

则我们能够把这一堆石子看成两堆,简称k堆和k-1堆。

(一定能够看成两堆,由于假如先手第一次取的石子数大于或等于f[k-1],则后手能够直接取完f[k],由于f[k] < 2*f[k-1])

对于k-1堆。由如果可知,不论先手如何取,后手总能取到最后一颗。以下我们分析一下后手最后取的石子数x的情况。

假设先手第一次取的石子数y>=f[k-1]/3。则这小堆所剩的石子数小于2y。即后手能够直接取完。此时x=f[k-1]-y,则x<=2/3*f[k-1]。

我们来比較一下2/3*f[k-1]与1/2*f[k]的大小。即4*f[k-1]与3*f[k]的大小,由数学归纳法不难得出,后者大。

所以我们得到,x<1/2*f[k]。

即后手取完k-1堆后。先手不能一下取完k堆,所以游戏规则没有改变,则由如果可知,对于k堆,后手仍能取到最后一颗,所以后手必胜。

即i=k+1时。结论依旧成立。

对于不是FIB数,首先进行分解。

分解的时候,要取尽量大的Fibonacci数。

比方分解85:85在55和89之间。于是能够写成85=55+30,然后继续分解30,30在21和34之间,所以能够写成30=21+9,

依此类推,最后分解成85=55+21+8+1。

则我们能够把n写成  n = f[a1]+f[a2]+……+f[ap]。

(a1>a2>……>ap)

我们令先手先取完f[ap],即最小的这一堆。因为各个f之间不连续。则a(p-1) > ap  + 1,则有f[a(p-1)] > 2*f[ap]。即后手仅仅能取f[a(p-1)]这一堆,且不能一次取完。

此时后手相当于面临这个子游戏(仅仅有f[a(p-1)]这一堆石子。且后手先取)的必败态。即先手一定能够取到这一堆的最后一颗石子。

同理可知。对于以后的每一堆,先手都能够取到这一堆的最后一颗石子,从而获得游戏的胜利。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=50;
long long f[50];
int main()
{
f[1]=1,f[2]=2;
for(int i=3;i<maxn;i++)
f[i]=f[i-1]+f[i-2];
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int sign=1;
for(int i=1;i<maxn;i++)
{
if(f[i]>n)
break;
if(f[i]==n)
{
sign=0;
break;
}
}
if(sign)
printf("A\n");
else
printf("B\n");
}
return 0;
}


51nod Bash游戏(V1,V2,V3,V4(斐波那契博弈))的更多相关文章

  1. 51nod 1070 Bash游戏 V4 (斐波那契博弈)

    题目:传送门. 有一堆个数为n(n>=2)的石子,游戏双方轮流取石子,规则如下: 1)先手不能在第一次把所有的石子取完,至少取1颗: 2)之后每次可以取的石子数至少为1,至多为对手刚取的石子数的 ...

  2. 51Nod 1070:Bash游戏 V4(斐波那契博弈)

    1070 Bash游戏 V4  基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 有一堆石子共有N个.A B两个人轮流拿,A先拿.每次拿的数量最少1个 ...

  3. 51Nod 1070 Bash游戏 V4(斐波那契博弈)

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1070 题意: 思路: 这个是斐波那契博弈,http://blog.csd ...

  4. {HDU}{2516}{取石子游戏}{斐波那契博弈}

    题意:给定一堆石子,每个人最多取前一个人取石子数的2被,最少取一个,最后取石子的为赢家,求赢家. 思路:斐波那契博弈,这个题的证明过程太精彩了! 一个重要的定理:任何正整数都可以表示为若干个不连续的斐 ...

  5. HDU 2516 取石子游戏(斐波那契博弈)

    取石子游戏 Time Limit: 2000/1000 MS(Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submissi ...

  6. HDU 2516 取石子游戏 斐波纳契博弈

    斐波纳契博弈: 有一堆个数为n的石子,游戏双方轮流取石子,满足: 1)先手不能在第一次把所有的石子取完: 2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍) ...

  7. HDU.2516 取石子游戏 (博弈论 斐波那契博弈)

    HDU.2516 取石子游戏 (博弈论 斐波那契博弈) 题意分析 简单的斐波那契博弈 博弈论快速入门 代码总览 #include <bits/stdc++.h> #define nmax ...

  8. 题解报告:hdu 2516 取石子游戏(斐波那契博弈)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2516 Problem Description 1堆石子有n个,两人轮流取.先取者第1次可以取任意多个, ...

  9. hdu 2516 取石子游戏 (斐波那契博弈)

    题意:1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍. 取完者胜,先取者负输出"Second win",先取者胜 ...

随机推荐

  1. C++ Primer高速入门之六:数组和指针

    更新:勘误,delete [] 猪 我们知道,C语言以及早期的面向结构的语言差点儿都支持数组定义.比方整形数组int 女神[2].表示有俩数: 女神[0], 女神[1].她们都是整数. C++ 语言为 ...

  2. Java编程思想(四) —— 复用类

    看了老罗罗升阳的专訪,不由自主地佩服,非常年轻,我之前以为和罗永浩一个级别的年龄.也是见过的不是初高中编程的一位大牛之中的一个,专訪之后.发现老罗也是一步一个脚印的人. 别说什么难做,做不了.你根本就 ...

  3. 用Maven创建SpringMVC项目

    IDE:Eclipse Jee JDK:8 Tomcat:8 1.创建项目 File->New->Maven Project-> ->Next-> ->Next-& ...

  4. POJ 2185 正解 KMP

    题意: 思路: 把每一行压成一个数 求一下 KMP 把每一列压成一个数 求一下KMP 答案就是两个周期之积 网上的好多题解都是错的---------.. //By SiriusRen #include ...

  5. Kinect 开发 —— 手势识别(下)

    基本手势追踪 手部追踪在技术上和手势识别不同,但是它和手势识别中用到的一些基本方法是一样的.在开发一个具体的手势控件之前,我们先建立一个可重用的追踪手部运动的类库以方便我们后续开发.这个手部追踪类库包 ...

  6. PythonOOP面向对象编程1

    什么是对象? 对象是指现实中的物体或实体(拥有一系列变量.函数(方法)的) 什么事面向对象? 把一切看成对象(实例),让对象和对象之间建立关联关系 对象都有什么特征? 属性(名词)实例变量 姓名.年龄 ...

  7. C# 将引用的DLL文件放到指定的目录下

    原文:C# 将引用的DLL文件放到指定的目录下 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sweety820/article/details/2 ...

  8. Android基础新手教程——3.8 Gestures(手势)

    Android基础新手教程--3.8 Gesture(手势) 标签(空格分隔): Android基础新手教程 本节引言: 周六不歇息,刚剪完了个大平头回来.继续码字~ 好的,本节给大家带来点的是第三章 ...

  9. POJ 题目1145/UVA题目112 Tree Summing(二叉树遍历)

    Tree Summing Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 8132   Accepted: 1949 Desc ...

  10. Android学习笔记进阶15之Shader渲染

    Android提供的Shader类主要是渲染图像以及一些几何图形. Shader有几个直接子类: BitmapShader    : 主要用来渲染图像 LinearGradient  :用来进行线性渲 ...