由K倍动态减法联想到的一类博弈问题 HDOJ 2580

来源:互联网 发布:iphone屏幕检测软件 编辑:程序博客网 时间:2024/05/16 18:25

给一堆石子,数量为n,先取的人最多能取n - 1个,之后每次最多能取上次对手取的石子的数量的k倍


当k = 1时必败数列为(1, 2, 4, 8 ……),当n的二进制中不止一位为1时,我们每次取n的二进制的最低一非0位数量,那么对手就绝对不能取完(因为高位的是地位的至少二倍),而对手不管取x为多少,假设x的最高位1为w,那么取后剩余石子n的w位或者w以下一定有1,那么我们就又可以取最低一位1了,依次进行,只有自己才能胜利。相反n的二进制只有一位,而我们又不能取完,相当于自己不能取最后一位非0位,对手就能胜。

当k = 2时必败数列为(斐波那契数列)(1,2, 3, 5, 8……),每个n都可以用数列中的不相邻数字相加得到,假设有至少两个数字(从小到大排序)(x1, x2……)相加,我们每次取最小的x1,因为x1,x2不相邻,及x1,x2至少相隔1个数字,观察斐波那契数列:隔1数字的两个数字大的是小的的至少两倍。那么对手绝对不能取完x2,对手取完后又得到一数字n1,依次进行,最后只有可能自己拿完石子。如果n及为数列中的数字,那么该n只能由一个x1相加组成,这时候我们不能取完,及不能取走最小的数字,相当于对手的胜局。


当k = n时,我们就要构造自己的数列


首先失败点为数列array(1),最大相加能组成的为max(1)

因为前面的array【i】最多能组成max【i】,max【i 】 + 1没法由数列中的数字相加组成

我们可以新建一个点,array【i + 1】 = max 【i】+ 1,解决问题;

新加了一个节点后最大能组成的数字应该为max【j】 + array【i + 1】;

因为要满足组成数字n的所有数字(x1, x2, x3……)相差至少k倍,所以array【j】满足array【j】 * k < array【i + 1】;

我们求的是最大能表示的数字,这时候只需要j足够大就可以了


很明显,j是随着失败数列中的数字的增多而增大的,由此优化算法,避免超时


HDOJ 2580 我的代码


#include<stdio.h>__int64 max[3000001], array[3000001];int main(){__int64 n, k, i, j;int cases, ca = 0;scanf("%d", &cases);while(cases --){scanf("%I64d%I64d", &n, &k);array[0] = 0;max[1] = 1;array[1] = 1;i = 1;j = 1;while(array[i] < n){i ++;array[i] = max[i - 1] + 1;while(array[j] * k < array[i])j ++;j --;max[i] = max[j] + array[i];if(i == 100000)printf("");}if(array[i] == n)printf("Case %d: lose\n", ++ca);else{while(1){if(n == array[i]){printf("Case %d: %I64d\n", ++ca, array[i]);break;}if(n > array[i]){n -= array[i];}else{i --;}}}}return 0;}

原创粉丝点击