Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
Nic和Susan在玩一个有趣的游戏:在游戏开始前,他们先约定一个正整数n,同时令m=1。游戏过程中,每个人都可以将m的值扩大2到9中的任意倍数。第一个使得m≥n的人就是最后的赢家。
假设Nic和Susan都十分聪明,并且游戏过程中都使用最佳的策略,问谁会是最后的赢家(Nic总是第一个先玩)。
【输入格式】
一个正整数n(1≤n≤2^32-1)。【输出格式】
在使用最佳的策略的情况下,如果Nic必胜,则输出“Nic”。如果Susan必胜,则输出“Susan”。否则输出“God”。
【数据规模】
Sample Input1
9
Sample Output1
Nic
【题解】
等价题意如下
给定N,Nic和Susan轮流将N整除2~9,第1个使N<=1的人赢。
Nic先手,问对于给定的N谁必胜。谁必胜
给定N,Nic和Susan轮流将N整除2~9,第1个使N<=1的人赢。
Nic先手,问对于给定的N谁必胜。谁必胜
设f[n]表示N为n时是先手赢还是先手输。
转移
假设x ∈{2,3,4,5,6,7,8,9};
如果n/x为整数。则M=n/x否则M= (n / x)+1 这里的除法是整除运算。直接去尾。
然后f[n] = f[n] || !f[M];
我们这里的讲解围绕那个等价题意展开。
F[1..9]都为true表示Nic只要除一下就能<=1了。所以是先手赢。
然后我们对F[t]考虑。
如果f[t/x](这里的x∈上面那个区间)是先手输。也就是说先手从t/x开始会输。
那么我们只要让t/x变成t就好了,然后就变成先手赢了。
因为你是t。然后除一个x就变成t/x了。然后先手就不是nic了。而是Susan.而f[t/x]==true表示在t/x时先手(这下变成Susan)一定会输。那不就等价为f[t]是先手(nic)赢了吗?
根据这样的规律我们可以写出以下程序
(注意:下面这个程序不足以过所有的数据);
memset(bo, false, sizeof(bo));
for (int i = 1; i <= 9; i++)
{
bo[i] = true;
}
for (int i = 10; i <= 21999999; i++)
for (int j = 2; j <= 9; j++)
{
int mm;
if ((i %j) == 0)
mm = i / j;
else
mm = (i / j) + 1;
if (!bo[mm])
bo[i] = true;
}
for (int i = 1; i <= 9; i++)
{
bo[i] = true;
}
for (int i = 10; i <= 21999999; i++)
for (int j = 2; j <= 9; j++)
{
int mm;
if ((i %j) == 0)
mm = i / j;
else
mm = (i / j) + 1;
if (!bo[mm])
bo[i] = true;
}
然后我们用这个程序求出bo[1..2000]的值。进而可以发现规律
—n =[1, 9],先手必胜
—n =[10, 18],先手必败即后手必胜
—n =[19, 162],先手必胜
—n =[163, 324],先手必败即后手必胜
也就是先手胜i = 9 if (n<=9) 先手
后手胜i=i*2 if (n <=i) 后手
先手胜i=i*9 if (n <=i) 先手
后手胜i=i*2 if (n <= i)后手
i会很大 ,要用long long
。。。
【代码】
/*
等价题意如下
给定N,Nic和Susan轮流将N整除2~9,第1个使N<=1的人赢。
Nic先手,问对于给定的N谁必胜。
*/
#include <cstdio>
long long n;
int main()
{
scanf("%lld", &n);
long long i = 9; //一开始i == 9然后小于9就是先手胜
int b = 1;
while (b != 2) //这个条件是不可能实现的 。所以就一直循环。
{
if (n <= i) //如果在我们找到的规律区间里就输出相应的答案 然后结束循环
{
if (b == 1)
printf("Nic\n");
else
printf("Susan\n");
break;
}
if (b == 1) //如果现在是先手胜的区间那么就直接*2
i = i * 2;
else //否则乘9;
i = i * 9;
b = 1 - b;
}
return 0;
}