【原创】POJ 3922 HDU 2486 HDU 2580 A simplestone game

POJ - 3922

Description

After he has learned how to play Nim game, Mike begins to try another stone game which seems much easier.

The game goes like this: Two players start the game with a pile of n stones. They take stones from the pile in turn and every time they take at least one stone. The one who goes first can take at most n − 1 n-1 n1 stones for his first move. From then on a player can take at most k times as many stones as his opponent has taken last time. For example, if one player take m stones in his turn, then the other player can take at most k ∗ m k * m km stones next time. The player who takes the last stone wins the game. Suppose that those two players always take the best moves and never make mistakes, your job is to find out who will definitely win the game.

Input

The first line contains a integer t t t, indicating that there are t t t test cases following. ( t &lt; = 20 ) (t&lt;=20) (t<=20).
Each test case is a line consisting of two integer n and k. ( 2 &lt; = n &lt; = 1 0 8 , 1 &lt; = k &lt; = 1 0 5 ) (2&lt;=n&lt;=10^8,1&lt;=k&lt;=10^5) (2<=n<=108,1<=k<=105)

Output

For each test case, output one line starting with “ C a s e N : Case N: CaseN:”, N N N is the case number. And then, if the first player can ensure a winning, print the minimum number of stones he should take in his first turn. Otherwise, print “ l o s e lose lose”. Please note that there is a blank following the colon.

Sample Input

5
16 1
11 1
32 2
34 2
19 3

Sample Output

Case 1: lose
Case 2: 1
Case 3: 3
Case 4: lose
Case 5: 4

Hint

When k = 1, the first player will definitely lose if the initial amount of stones is in the set {2, 4, 8, 16, 32, …}. Let’s call this kind of set “first-player-lose set”.

When k = 2, the first-player-lose set is {2, 3, 5, 8, 13, 21, 34, 57 …} , which happens to be the Fibonacci sequence starting from 2.


分析

我太难了。

K=1

如果 K = 1 K=1 K=1,无解的答案为 2 n 2^n 2n。先手的必胜策略是取 n n n的最后一位 1 1 1,如 ( 25 ) 1 0 = ( 11001 ) 2 (25)_10=(11001)_2 (25)10=(11001)2就取走 1 1 1个石子, ( 24 ) 1 0 = ( 11000 ) 2 (24)_10=(11000)_2 (24)10=(11000)2,就取走 8 8 8个石头;这样对手无论如何取都会把若干个 0 0 0取为 1 1 1,然后你再取 n n n的最后一位。这样保证了始终是你把 1 1 1态变为 0 0 0态,对手把 0 0 0态变为 1 1 1态。周而复始,最后把石头取完的一定是你。


K=2

如果 K = 2 K=2 K=2,就是经典的斐波那契博弈问题。无解的答案为斐波那契数。

证明如下:
如果 n n n为斐波那契数 f i f_i fi,考虑数学第二归纳法证明:

如果 i = 3 , f i = 2 i=3,f_i=2 i=3fi=2,先手只能取一颗,显然必败;
假设对于 i &gt; 3 i\gt 3 i>3 ∀   3 ≤ j &lt; i \forall ~ 3\leq j \lt i  3j<i都有 “ n = f j 时 是 先 手 必 败 态 ” “n=f_j时是先手必败态” n=fj成立,现在只需证明 f i = f i − 2 + f i − 1 f_i=f_{i-2}+f_{i-1} fi=fi2+fi1也是必败态。

稍微感性一点的理解的话:

如果我先手取的石头数量比较大,比如说取了 ≥ f i − 2 \geq f_{i-2} fi2颗石头,那么后手就会赢。
因为 f i − 1 &lt; 2 ∗ f i − 2 f_{i-1}&lt;2*f_{i-2} fi1<2fi2,剩下的石头至多是 f i − 1 f_{i-1} fi1颗,而这是先手2倍以内,可以被后手一步取完。

如果我先手取的石头比较少,比如取了 &lt; f i − 2 \lt f_{i-2} <fi2颗石头,那么后手还是会赢。
因为由我们之前的假设可知,对于前 f i − 2 f_{i-2} fi2的最后一颗石头(相当于一个 f i − 2 f_{i-2} fi2的子问题,而这已经在我们第二归纳法的假设中假设成立了)肯定可以由后手取到,此时留给我们先手的是 f i − 1 f_{i-1} fi1颗石头,而这也已经被假设为必败态了,先手 G G GG GG了。

你说,当来到 f i − 1 f_{i-1} fi1颗石头的时候,因为上一局后手已经取过了,先手的取石子数有限制,这怎么办?
这不怎么办。必败态就是决定了你无论怎么取石头都会输的必败态,怎么挣扎都是 木大木大哒 \colorbox{#ffff00}{\color{RoyalBlue}{木大木大哒}} 的。

接下来证明所有非斐波那契数都是必胜态。
由齐肯多夫定理知,任意一个正整数都可以表示为若干个不相邻的斐波那契数之和(相邻的就可以直接合成为一个新的斐波那契数了,这样不断合成直至最后就可以不相邻了)。
n = f a 1 + f a 2 + ⋯ + f a k ( 假 设 是 从 小 到 大 排 序 ) n=f_{a_1}+f_{a_2}+\cdots+f_{a_k}(假设是从小到大排序) n=fa1+fa2++fak,如 9 = 1 + 8 , 19 = 1 + 5 + 13 9=1+8,19=1+5+13 9=1+819=1+5+13

那么我们先手先取走 f a 1 f_{a_1} fa1,因为不相邻,所以有 f a 2 &gt; 2 ∗ f a 1 f_{a_2}&gt;2*f_{a_1} fa2>2fa1,故后手无论如何取都不能把 f a 2 f_{a_2} fa2取完,于是这里又变成了一个 f a 2 f_{a_2} fa2的子问题,由上文知,这 f a 2 f_{a_2} fa2的最后一颗石头一定是我先手取到。同理,对于每个 f a i f_{a_i} fai的子问题,都是后手在面对,而最后一颗总是在我手上。

仔细想想,发现K=1和K=2的情况是很类似的,二者的不同就在于一个是用 2 i 2^i 2i数列,一个是用斐波那契数列。


任意的K

对于 K &gt; 2 K\gt 2 K>2的情况。
K = 2 K=2 K=2的情况得到启发,能不能把 n n n写成若干数的和, n = a 1 + a 2 + ⋯ + a k n=a_1+a_2+\cdots+a_k n=a1+a2++ak,进行类似的操作?
(即我取走 a 1 a_1 a1,就有:你不能一步取完 a 2 a_2 a2,并且经过若干步以后, a 2 a_2 a2的最后一颗石头总是我的)

如果存在一种类似于 2 i 2^i 2i数列和斐波那契数列的数列 { a } = { a 1 , a 2 , a 3 , ⋯ &ThinSpace; } \{a\}=\{a_1,a_2,a_3,\cdots\} {a}={a1,a2,a3,},使得任何正整数 n n n能用 { a } \{a\} {a}中的数求和得到,并且我们选中的数 a 1 + a 2 + ⋯ + a k ( 假 设 从 小 到 大 排 序 ) a_1+a_2+\cdots+a_k(假设从小到大排序) a1+a2++ak中, ∀   1 ≤ i &lt; k , 有 a i + 1 &gt; K × a i \forall ~1\leq i\lt k,有a_{i+1}&gt;K\times a_i  1i<kai+1>K×ai,我们就可以用类似的方法:如果在这个数列中的数是必败态,而不在其中的,先手取最小的那一部分然后必胜,来判断是否能赢。

这个数列怎么找呢……
首先,我们记录一个数组 b i − 1 b_{i-1} bi1,代表只用前 i − 1 i-1 i1项能够构造出的最大的数。
比如 K = 2   K=2~ K=2 时, a = 1 , 2 , 3 , 5 , 8 , 13 , 21 , ⋯ {a}={1,2,3,5,8,13,21,\cdots} a=1,2,3,5,8,13,21,,那么 b 3 b_3 b3代表只用 1 , 2 , 3 \bold {1,2,3} 1,2,3的斐波那契数能构造的最大数,即 4 = 1 + 3 4=1+3 4=1+3,而非 5 = 2 + 3 5=2+3 5=2+3,也不是 6 = 1 + 2 + 3 6=1+2+3 6=1+2+3,因为 5 = 5 5=5 5=5 6 = 1 + 5 6=1+5 6=1+5
不过当 K &gt; 2 K\gt 2 K>2时,我们不知道 5 5 5 6 6 6是不是,我们可以令 b 3 = 6 b_3=6 b3=6,之后选 a 4 a_4 a4的时候不选5就是了。

然后再次使用归纳法。
假设已经找到了前 i − 1 i-1 i1项: a 1 , a 2 , a 3 , ⋯ &ThinSpace; , a i − 1 a_1,a_2,a_3,\cdots,a_{i-1} a1,a2,a3,,ai1,也求出了 b 1 , b 2 , b 3 , ⋯ &ThinSpace; , b i − 1 b_1,b_2,b_3,\cdots,b_{i-1} b1,b2,b3,,bi1。因为我们要让任意自然数都能组合出来,但我们最大只能造出 b i − 1 b_{i-1} bi1,更大一点儿的数,比如 b i − 1 + 1 b_{i-1}+1 bi1+1怎么组合出来呢?当然不能组合出来呢, b b b数组已经决定了这一点。那么,为了把 b i − 1 + 1 b_{i-1}+1 bi1+1组合出来,我们不得不把 b i − 1 + 1 b_{i-1}+1 bi1+1加入 a a a数组中。
于是得到 a i = b i − 1 + 1 a_i=b_{i-1}+1 ai=bi1+1

现在我们有了 a i a_i ai,考虑怎么求 b i b_i bi。因为我们构造出的 b i = a c 1 + a c 2 + ⋯ + a c k b_i=a{c_1}+a_{c_2}+\cdots+a_{c_k} bi=ac1+ac2++ack要满足 ∀   1 ≤ z &lt; k , 有 a c z + 1 &gt; K × a c z \forall ~1\leq z\lt k,有a_{c_{z+1}}&gt;K\times a_{c_z}  1z<kacz+1>K×acz。于是我们先选上 a i a_i ai,再往前找到第一个满足 a i &gt; K × a t a_i&gt;K\times a_t ai>K×at的t,于是就得到 b i = a i + b t b_i=a_i+b_t bi=ai+bt

为什么要先选上 a i a_i ai呢?
这是显然的……因为不用上 a i a_i ai,你就是在求 b i − 1 b_{i-1} bi1……
然后选上了 a i a_i ai以后就是一个简单的贪心……

为什么这样做能保证所有不在 { a } \{a\} {a}里的数都能被选中?
因为由于定义, b i b_i bi以前的所有数都可以被组合出来,而 b i b_i bi以后是直接+1得到 a i + 1 a_{i+1} ai+1,然后由于定义, [ b i , b i + 1 ] [b_i ,b_{i+1}] [bi,bi+1]的数都可以组合出来。然后继续重复……所以只统计最大是可以关照到所有的数的……
关键问题是为什么这么定义就是对的?也就是问为什么 b i b_i bi可以 并且 b i + 1 b_{i+1} bi+1可以 就能保证 [ b i , b i + 1 ] [b_i ,b_{i+1}] [bi,bi+1]都可以?
emmm……荒废了一个小时以后发现我的能力不足以独立证明……
为什么能保证 n = b i + t , 1 ≤ t ≤ b i + 1 − b i n=b_i+t,1\leq t\leq b_{i+1}-b_{i} n=bi+t,1tbi+1bi能由不重复的 { a } \{a\} {a}组合出来呢?是分为 b i b_i bi t t t两部分吗,但这又怎么保证不重复呢?
如果K=2时斐波那契数可以直接合并,那么 K ≠ 2 K≠2 K̸=2时呢?
其实我们上述的操作已经类似于求证齐肯多夫定理了…………所以请参见广义齐肯多夫定理?

P.S.这篇博客就是求证了一遍齐肯多夫定理再拓展到广义齐肯多夫定理……


代码实现

于是你就把 { a } &amp; { b } \{a\}\And \{b\} {a}&{b}手跑出来……然后看 n n n是不是其中一项……

#include<cmath>  
#include<cstdio> 
#include<cstring>  
#include<algorithm>  
using namespace std;  

void Read(int &p)
{
	p=0;
	char c=getchar();
	while(c<'0' || c>'9') c=getchar();
	while(c>='0'  && c<='9') 
		p=p*10+c-'0',c=getchar();
}

const int MAXN=2002030;
int t,n,k,a,b,id,ans,arr[MAXN],brr[MAXN];

int main()
{
	Read(t);
	while(t--)
	{
		id++;
		Read(n),Read(k);
		if(n<=k+1){printf("Case %d: lose\n",id);continue;}
		
		a=0,b=0,arr[0]=brr[0]=1;
		while(arr[a]<n)
		{
			a++,arr[a]=brr[a-1]+1;
			while(arr[b+1]*k<arr[a]) b++;
			brr[a]=arr[a]+(arr[b]*k<arr[a])*brr[b];
		}
				
		if(arr[a]==n) {printf("Case %d: lose\n",id);continue;}

		while(n)
		{
			if(n>=arr[a]) ans=arr[a],n-=arr[a];
			a--;
		}
		printf("Case %d: %d\n",id,ans);
	} 
}

代码里要有个小优化……

说在后面

为什么你的博客里要有这么多无意义的显然的自问自答?
因为这是我在想的时候真的考虑到的东西,卡了好久发现啊真显然啊太没意义了啊……

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值