hdoj 2211 杀人游戏 【约瑟夫环】

杀人游戏

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

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
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
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;
}

 
 

你可能感兴趣的:(小窍门和公式)