题目链接 A Sample Stone Game

  题目大意:给定n,k,表示最初时有n个石头,两个人玩取石子游戏,第一个人第一次可以取1~n-1个石头,后面每个人最多可以拿走前面一个人拿走的个数的K倍,当有一个人可以一次性全部拿走时获胜。问两人都在不失误的情况下,先拿着有没有必胜局势。有的话求他第一次最少该取多少个。

思考过程:

首先讨论k=1的情况,我们可以把一个数n(石子的个数),写为二进制下的表示,那先者取走最后一个1,那后者必然不能取走比它高一位的1,那么先拿者一定会赢,当然如果n本来就是2^i形式,那么先拿着当然不能拿走最后一个1,这样的话,石子就会取完了,所以这时先取者必输。举个例子来说 N = 20,他的二进制表示就是

1、10100              先拿者A拿走最后一个1(相当于拿走4个)n变为

2、10000              这时后取者B只能拿m=1,2,3,4个,假如是拿走3个,那么n变为

3、01101              这样A又可以取走最后一个1,。

... ...                 这样继续下去,当这个数n变为1时

00001,            最后一个1一定还是A拿走的,所以A必赢

而其实第2步中,不管B拿走多少个(即不管m是多少m = 1 or 2 or 3 or 4),在她之后的那个人又一定可以取走最后一个1(即取走x = (1<<y)),且这个数x一定小于等于m(定理1),这样的话B永远也不可能取完。

  而如果最初,n就等于2^j,二进制就是           10000...               那A自然不可能取走最后一个1(那就取了n个了),这样的话A取走一个m剩下的数就转化为上面,第1步中的形式了,那B一定就是必赢了

然后是k=2时由定理2可以知道任何一个非菲波拉契数n都可以写为多个两两不相邻的斐波拉契数的和,这样的话先取者A先拿走一个小的斐波拉契数x,那后取者就无法取到另一个较大的菲波拉契数y(因为x与y不相邻,所以y>2*x)。这样又给A留下了一个非斐波拉契数,一直递归下去,A一定就是赢者。

  但同时,如果A最初面临的就是一个菲波拉契数Fn,但是Fn只能写为Fn-1 + Fn-2,如果取Fn-2,由于Fn-1 < 2 * Fn-2,所以B可以取完,如果取其他任意一个非斐波拉契数m,B得到的那剩下的数Fn-m就相当于上面的A最初面临的n,也就是说B必赢。

 

而对于一般的K,由上面的思路,我们的目的就是要让n写为一些数的和n1 + n2 + ...+np,而且它们两两相邻之间的倍数关系应该大于k,这样的话我们先者A取走小一点的数n1,后者B必然不能取走n2,就给A留下了一些数,这样的话B自然不能取完所以A获胜。

  在解题时,那我们就应该求出某个数列a[],这个数列要满足任何一个不再这个数列里面的数都可以用这个数列里的(满足前提条件前一项的k倍小于后一项的)多项的和表示(就好似定理2中任何一个数都可以写为多个个不连续的菲波拉契数(不连续就是相当于前一个的k=2倍小于后一个数)的和是同样的原理)。

  而为了要求出这个数组a,我们引入另一个数组b[],b[i]表示a[0],a[1]...a[i]可以找到两项a[x]和a[y]满足a[x] * k < a[y],且可以构造出的数(即a[x]+a[y])的最大值。

  假设a的前i项已经求出,由于前i项最大已经可以构造出b[i],那么b[i]+1就无法被构造出来,所以需要另外开一项a[i+1] = b[i]+1。这时我们要求b[i+1]由于b[i+1]表示的是前i+1个a可以构造出来的最大的数所以一定得用到a[i+1],同时又要满足必须存在一项a[x]使得a[x]*k<a[i+1],所以我们就从之前的a中找出这个最大的a[x],那么b[i+1]=a[x]+a[i+1]。但是有可能之前数据太少,找不到a[x] * k < a[i+1];那也就是相当于a[i+1]无法构造出来,因此b[i+1] = a[i+1](例如i=0,a[x]只能取a[0],而a[0]*k>=a[1],那么b[1]只能等于a[1])。 提炼出来就是:

if(a[x]*k<a[i+1])

  b[i+1] = a[x] + a[i+1];

else

  b[i+1] = a[i+1];

  然后就是要解决第一步取得最少且获胜的数量,那么先从n里减去小于n的最大的a[],然后又递归下去求这个剩下的n里最大的a[],找到n==a[]的a[]。也就是说这是这一串和里面最小的一个a了,将他输出就行了

定理1:对任意一个不可以写为2^k(k>=1)的数n,从它的二进制表达式中先取走最后一个1(即拿走m_A = (1<<k1)),得到一个数n1,那么再从n1中取走m_B(m_B<=m_A)个,得到n2。那么n2的二进制中最后一位1(第k2位)所在的位置所表示的数一定小于等于m_B((1<<k2) <= m_B)。

证明:我们可以把m_B写为二进制形式,而它的二进制中所有的1所在的位置我们记为a1,a2,a3....ak(k>=1)(a1<a2<a3<...<ak),也就是说m_B = (1<<a1)+(1<<a2)+...+(1<<ak)。那我们用n1 - m_B得到n2,由于在n1中a1位置是0,a1以下的所有位置都是0,而m_B中a1位置是1,m_B在a1以下位置也都是0,所以相减后得到的n2在a1位置一定是1。用二进制表示就是:

n             100...100...100000...          拿走最后一个1(m_A = (1<<k1)),得到

n1            100...100...000000...          再随便找一个m_B<=m_A

m_B           000...000...01010...          加粗部分从左至右为ak,ak-1...a2,a1,然后用n1 - m_B得到

n2            100...011...101010...          n2在m_B最后一个1的位置(蓝色位置)也必定为1

定理2:任何一个非斐波拉契数都可以写为若干个不相邻的菲波拉契数的和,任何一个菲波拉契数都不能写为两个非相邻的斐波拉契数的和(原因很明显,因为Fn = Fn-1 + Fn-2,Fn-1与Fn-1相邻)

上面的思想过程转自神牛http://hi.baidu.com/lccycc_acm/item/a6f0dd0ec5c44a39f3eafcd3

有了上面的思想,代码实现就很简单了

 #include <stdio.h>

 int a[],b[];
int main()
{
int n,k;
int T = ,Case = ;
while(~scanf("%d",&T))while(T--)
{
scanf("%d%d",&n,&k);
a[] = b[] = ;
int i=,j=;
while(n > a[i])
{
i++;
a[i] = b[i-] + ;
while(a[j+] * k < a[i])
{
j++;
}
if(k * a[j] < a[i])
{
b[i] = b[j] + a[i];
}
else b[i] = a[i];
}
printf("Case %d: ",Case++);
if(n == a[i])printf("lose\n");
else
{
int ans ;
while(n)
{
if(n >= a[i])
{
n -= a[i];
ans = a[i];
}
i --;
}
printf("%d\n",ans);
} }
return ;
}

POJ 3922A Simple Stone Game的更多相关文章

  1. POJ 3922 A simple stone game

    题目: E - A simple stone game Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d &am ...

  2. Something about 博弈(POJ 3922 A simple stone game)

    先是题目,本来是第三次训练的题,在这特别提出来讲. 先是题目: E - A simple stone game Time Limit:1000MS     Memory Limit:65536KB   ...

  3. HDUOJ--------A simple stone game(尼姆博弈扩展)(2008北京现场赛A题)

    A simple stone game                                                                                  ...

  4. HDU6237-A Simple Stone Game-找素因子(欧拉函数)-2017中国大学生程序设计竞赛-哈尔滨站-重现赛

    A Simple Stone Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Ot ...

  5. 2017中国大学生程序设计竞赛-哈尔滨站 H - A Simple Stone Game

    A Simple Stone Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Ot ...

  6. HDU 6237.A Simple Stone Game-欧拉函数找素因子 (2017中国大学生程序设计竞赛-哈尔滨站-重现赛)

    A Simple Stone Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Ot ...

  7. uva 1567 - A simple stone game(K倍动态减法游戏)

    option=com_onlinejudge&Itemid=8&page=show_problem&problem=4342">题目链接:uva 1567 - ...

  8. hdu 2486/2580 / poj 3922 A simple stone game 博弈论

    思路: 这就是K倍动态减法游戏,可以参考曹钦翔从“k倍动态减法游戏”出发探究一类组合游戏问题的论文. 首先k=1的时候,必败态是2^i,因为我们把数二进制分解后,拿掉最后一个1,那么会导致对方永远也取 ...

  9. Poj 3468-A Simple Problem with Integers 线段树,树状数组

    题目:http://poj.org/problem?id=3468   A Simple Problem with Integers Time Limit: 5000MS   Memory Limit ...

随机推荐

  1. bzoj3244

    很不幸,这题我又被虐了,给个链接http://www.cnblogs.com/g-word/p/3288675.html ..] of longint; f:..,..] of longint; mx ...

  2. 从客户端中检测到有潜在危险的 Request.Form 值-解决方案

    环境:VS2010 1.页头上加上ValidateRequest="false" <%@ Page Language="C#" ValidateReque ...

  3. Android-Universal-Image-Loader

    基本以后都不用了,所以自己就不总结了 http://www.cnblogs.com/kissazi2/p/3886563.html http://www.cnblogs.com/kissazi2/p/ ...

  4. 原创:js代码, 让dedecms支持Tag选择, 添加内容更为方便,不用手输Tag

    dedecms在编辑修改内容时,TAG标签需要手动输,中文的Tag, 中间还得用半角字符','分隔,  输入法切来切去很不方便,   于是动手改后台代码, 利用后台的tags_main.php, 让d ...

  5. POJ 2942 Knights of the Round Table (点双连通分量)

    题意:多个骑士要开会,3人及以上才能凑一桌,其中部分人已经互相讨厌,肯定不坐在同一桌的相邻位置,而且一桌只能奇数个人才能开台.给出多个人的互相讨厌图,要求多少人开不成会(注:会议不要求同时进行,一个人 ...

  6. apache开源项目 -- tez

    为了更高效地运行存在依赖关系的作业(比如Pig和Hive产生的MapReduce作业),减少磁盘和网络IO,Hortonworks开发了DAG计 算框架Tez.Tez是从MapReduce计算框架演化 ...

  7. Java [Leetcode 338]Counting Bits

    题目描述: Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculat ...

  8. Java [Leetcode 328]Odd Even Linked List

    题目描述: Given a singly linked list, group all odd nodes together followed by the even nodes. Please no ...

  9. 【unity3D】鼠标控制camera的移动、放大(俯视浏览效果、LOL摄像机移动效果)

    在Unity开发中,用鼠标滚轮来实现摄像机的视野范围,鼠标左键拖拉控制摄像机的移动,代码如下: 1.俯视浏览效果 using UnityEngine; using System.Collections ...

  10. Android设计模式源码解析之桥接模式

    模式介绍 模式的定义 将抽象部分与实现部分分离,使它们都可以独立的变化. 模式的使用场景 如果一个系统需要在构件的抽象化角色和具体化角色之间添加更多的灵活性,避免在两个层次之间建立静态的联系. 设计要 ...