HDU 2580 a simple stone game K倍减法博弈游戏

a simple stone game

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 357    Accepted Submission(s): 253


Problem 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 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 taken at most k * m 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,indicating that there are t test cases following. (t<=20).Each test is a line consisting of two integer n and k.(2<=n<=10^8,1<=k<=10^5).
 

Output
For each test case,output one line starting with“Case 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 “lose ”.Please note that there is a blank following the colon.
 

Sample Input
  
  
6 16 1 11 1 32 2 34 2 19 3 100000000 100000
 

Sample Output
  
  
Case 1: lose Case 2: 1 Case 3: 3 Case 4:lose Case 5: 4 Case 6: 912
Hint
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的情况

把每个数n写成二进制的形式

从必败态(1<<t)这个数,它是必败的局面,因为从它出发一定会得到必胜的局面(这是显然的,因为这个数不管减去多少,都会形成一个至少有2位是1的二进制数)。

从必胜态(至少有2位是1)出发,当前这个人小A取走最低位的1。由于k=1,所以接下来的人小B一定不会取到更高位的1,因此必然小A可以取走最后的一个1使得局面变成必败态(1<<t)的类型。

k=2的情况也是类似,每个正整数都可以被表示成fib数列中不相邻且不相等的数的和。(这是重点!解这道题目的核心部分)

我们容易知道,fib数列中不相邻的项至少是两倍的关系。所以说,如果我们把n用fib数列来编码的话,那么我们从必胜态取走最低位一个1的时候,另一个人显然也不能取走更高位的1,这样的话,我一定会保证取走最后一个1使得局面 重回必败态。

k>2的时候我们就需要来构造这个序列了,基于动态规划的思想。(哇,这个想法好腻害)

这个想法也基于一个非常重要的性质,那就是每个局面n必须可以被必败数列编码,并且!,注意被编码形成的必败局面的子序列中相邻两项 倍数必须大于K,也就是K*a[ti]<a[ti+1]

我们假设a[i]是必败数列中的第i项,b[i]表示用a数列的前i项可以按照以上规则编码1~b[i]里的数,我们可以得到递推公式。

b[i]+1显然不能由a的前i项来编码,所以我们再构造一个a[i+1]使得a[i+1] = b[i]+1。

然后我们 考虑用前i+1项最大可以编码到多少数,如果我们要用a[i+1]的话,那么前面的a的项数中能用的必然它的k倍不能大于等于a[i+1],也就是说前面项中最大的a[j]必然要满足

K*a[j] < a[i+1]而a[j]能编码的最大的数为b[j],所以a[i+1]和前面的项能变吗的最大的数就是b[i+1] = b[j] +a[i+1]

如果从前面找不到这样的a[j]怎么办呢。那么b[i+1]显然就只能等于a[i+1]了,也就是(b[i+1] = b[i]+1),只增大了1个数。

这里还存在一个编码的问题,如果胜利了,就要输出编码的lowbit,直接从大到小循环就好了,判断n在b[i]和b[i-1]之间,那么一定会被a[i]编码。

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2000000;
int n,k;
int a[MAXN],b[MAXN];
int main(){
	int T,cas = 0;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);
		a[0] = 1;
		b[0] = 1;
		int j = -1;
		int i = 1;
		for(;a[i-1] < n;i++){
			if(a[i-1] >= n) break;
			a[i] = b[i-1] + 1;
			while(a[j+1] * k < a[i]) j++;
			if(a[j] < a[i]) b[i] = b[j] + a[i];
			else b[i] = a[i];
		}
		if(n == a[i-1]){
			printf("Case %d: lose\n",++cas);
		}
		else{
			int last = 0;
			for(int t = i-1;t >= 0;t--){
				if(b[t] >= n && n > b[t-1]){
					last = a[t];
					n -= a[t];
				}
			}
			printf("Case %d: %d\n",++cas,last);
		}
	}
	return 0;
} 








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值