POJ 3922 A simple stone game(齐肯多夫表述法)

版权声明:没人会转的( ̄▽ ̄") https://blog.csdn.net/j2_o2/article/details/81866985

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 take 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 case is a line consisting of two integer n and k.(2<=n<=108,1<=k<=105).

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

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


题意

有一个整数 n(>=2),第一步先手在 n 上减掉一个数 x(0 < x < n ),之后双方轮流把 n 减掉一个正整数,不能超过先前一回合对方减掉的数的 k 倍,减到 0 的一方获胜。如果先手必败输出lose,否则输出第一步减少最小数量石子。

思路

以下全是口胡
从“k倍动态减法游戏”出发探究一类组合游戏问题 这篇论文介绍了O(n)的解法,但对于此题还是不够的,不过可以观察发现在其中每一项f[i] == i的情况之间与k有关,代码仅供参考。

#include<stdio.h>

#define N 100000005

int f[N], sta[N];

int main()
{
    int __, case_time = 1;
    f[1] = 1;
    for(scanf("%d",&__); __; --__)
    {
        int n, k;
        sta[1] = 1;
        scanf("%d%d",&n,&k);

        for(int i = 2, j = 1; i <= n; ++i)
        {
            while(j && (i-sta[j])*k >= f[sta[j]]) --j;
            f[i] = i-sta[j];
            sta[++j] = i;
        }
        for(int i = 1; i <= 30; ++i)
        {
            printf("%d:\t",i);
            for(int j = 1; j < f[i]; ++j) printf("0 ");
            printf("1\n");
        }
    }
    return 0;
}

这篇介绍了一种更快的解法 ,运用齐肯多夫定理,类似的将一个数n写成齐肯多夫表述的形式。需要满足任意数拆分的 唯一性,且n拆分后所选取项之间的任意两项之间倍数大于k。
第一步取走最小拆分即可胜利,如果最小拆分为本身即失败。详见上述链接

是不是没看懂因为,都是口胡ಥ_ಥ,还是看链接中那两位dalao的文章吧


ac代码

#include<stdio.h>

int f[1000005];

int main()
{
    int __, case_time = 1;
    f[1] = 1;
    for(scanf("%d",&__); __; --__)
    {
        int n, k, i, j;
        scanf("%d%d",&n,&k);

        for(i = 1, j = 1; f[i] <= n; ++i)
        {
            while(1ll*f[j]*k < 1ll*f[i]) ++j;
            f[i+1] = f[i] + f[j];
        }
        if(f[--i] == n) printf("Case %d: lose\n",case_time++);
        else
        {
            while(n != f[i])
            {
                n -= f[i];
                while(n < f[i]) --i;
            }
            printf("Case %d: %d\n",case_time++,n);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/j2_o2/article/details/81866985