jzoj3033. 【NOIP2012模拟10.17】石子游戏

本文探讨了博弈论在解决石子游戏问题中的应用,通过分析不同情况下(如k=1, k=2及k>2)的必败局面,介绍了如何构造数列来判断先手是否有必胜策略,并提供了具体的解题思路与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

jzoj3033. 【NOIP2012模拟10.17】石子游戏

题目

Description

两人取一堆n个石子 先手不能全部取完 之后每人取的个数不能超过另一个人上轮取的数*K。取完最后一个石子的人获胜。给n,K判断先手必胜并求第一步。

Input

第一行为一个正整数t(1<=t<=10),表示共t组测试数据
接下来t行,每行包括两个正整数n,k

Output

共t行,第i行先输出“Case i: ”(不包括引号),接着输出结果,若先手有必胜策略则输出第一次取的石子数(答案不唯一,输出第一步最小选几),否则输出lose

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

对于10%的数据k=1;
对于30%的数据1<=k<=2;
对于100%的数据2<=n<=100000000,1<=k<=100000。

分析

这是一道博弈题,比赛时连k=1的情况的分都没拿到,完全不会。

  1. 当k=1的时候 可知必败局面都是2^i 将n分解成二进制,然后先手取掉最后一个1。然后对方必然无法去掉更高的1,而对方取完我方至少还能拿掉最后一个1 导致对方永远取不完。

  2. 当k=2的时候,必败局面都是斐波那契数列。利用“先手去掉最后一个1,则后手必不能去掉更高阶的1导致取不完”的思想,斐波那契数列有一个非常好的性质就是:任意一个整数可以写成斐波那契数列中的不相邻的项的和,于是将n写成这种形式,先取走最后一个1,对方能取的数是这个数*2,小于高2位的1,所以取不完。

  3. 当K>2的时候, 想办法构造数列,将n写成数列中一些项的和,使得这些被取到的项的相邻两个倍数差距>k 那么每次去掉最后一个1 还是符合上面的条件。
    设这个数列已经被构造了i 项,第 i 项为a[ i ],前 i 项可以完美对1…b[ i ] 编码使得每个编码的任意两项倍数>K 那么有a[ i+1 ] = b[ i ] + 1;(这是显然的)
    因为b[ i ] + 1没法构造出来,只能新建一项表示,然后计算b[ i+1]
    既然要使用 a[ i+1 ] 那么下一项最多只能是某个 a[ j ] 使得 a[ j ] * K < a[ i+1 ] 于是b[ i ] = b[ j ] + a[ i+1 ]
    然后判断n是否在这个数列里面
    如果在,那么先手必败。否则不停的减掉数列a中的项构造出n的分解,最后一位就是了。

CODE

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

int t,n,k,a[1000000],b[1000000];

int main(){
	scanf("%d",&t);
	for (int l=1;l<=t;l++){
		scanf("%d%d",&n,&k);
 		int i=0,j=0;
		a[0]=b[0]=1;
		while (a[i]<n){
			i++;
			a[i]=b[i-1]+1;
  			while (a[j+1]*k<a[i]) j++;
			if (a[j]*k<a[i]) b[i]=a[i]+b[j];
				else b[i]=a[i];
		}
		if (a[i]==n) printf("Case %d: lose\n",l);
		 	else{
				int m;
			 	while (n){
					if (n>=a[i]){
					  	m=a[i];
					  	n-=a[i];
				 	}
				 	i--;
				}
				printf("Case %d: %d\n",l,m);
			}
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值