杀人游戏
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1877 Accepted Submission(s): 441
Problem Description
不知道你是否玩过杀人游戏,这里的杀人游戏可没有法官,警察之类的人,只有土匪,现在已知有N个土匪站在一排,每个土匪都有一个编号,从1到N,每次杀人时给定一个K值,从还活着的土匪中,编号从小到大的找到K个人,然后杀掉,继续往下,直到找遍,然后继续从剩下的土匪中,编号从小到大找到第K个活着的土匪,然后杀掉。比如,现在有10个土匪,K为3,第一次杀掉3,6,9号的土匪,第二次杀掉4,8号土匪,第三次杀掉5号土匪,第四次杀掉7号土匪,第五次杀掉10号土匪,我们看到10号土匪是最后一个被杀掉的(从1到K-1的土匪运气好,不会被杀!)。现在给定你一个N和一个K,问你最后一个被杀掉的土匪的编号是多少。
Input
第一行有一个T(T<=10000),接下来有T组数据,每组中包含一个N(N<2^31)和一个K(3<=K<=100&&K<N)。
Output
对于每组数据,输出最后被杀的土匪的编号。
Sample Input
110 3
Sample Output
10
解决这道题目我们可以采用倒退的方法。
我们假设最后一个被杀的人在当前序列的编号为pos,上一轮杀掉num个人,那么可以得到最后被杀的人在上一轮序列的编号 = pos + num;
现在我们要做的是求出num:因为最后一轮剩余pos个人,且编号为1的人不受任何影响。(我们在这里不讨论k=1的情况,没意思。。。)那么我们就可以知道除去第一个人,余下的 pos - 1 个 人是经过上一轮每 k 人杀一个剩下的,则有num = ( pos-1+num ) / k,化简后得到num = ( pos - 1 ) / ( k-1 )。
我们就得到一个公式:
最后被杀的人在上一轮的编号 = 该人本轮的编号 + (该人本轮的编号 - 1 ) / ( k - 1 );
即:pos = pos + (pos - 1) / (k - 1)
附上两种代码:
模拟过程:
#include<stdio.h>
int main()
{
int t,n,k;
int pos;
int man;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
pos=k;//最后被杀的编号
for(man=n;man>k;man-=man/k)//模拟人数从n到k+1的过程
pos=pos+(pos-1)/(k-1);//依次推导上一次被杀的编号
printf("%d\n",pos);
}
return 0;
}
从后往前递推:
#include<stdio.h>
int f(int n,int k)//记录n个人依次杀掉编号为k的人 最后一个被杀的编号
{
if(n==k)
return k;
int t=f(n-n/k,k);
return t+(t-1)/(k-1);
}
int main()
{
int t,n,k;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
printf("%d\n",f(n,k));
}
return 0;
}