Joseph

来源:互联网 发布:无线网测试软件 编辑:程序博客网 时间:2024/05/17 08:20

Joseph
lime Limit: 1000msMemory Limit: 32768KB This problem will be judged on HDU. Original ID: 1443
64-bit integer IO format: %I64d Java class name: Main
Prev Submit Status Statistics Next
Type:
None

Tag it!
The Joseph’s problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, …, n, standing in circle every mth is going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give us the message about the incident. For example when n = 6 and m = 5 then the people will be executed in the order 5, 4, 6, 2, 3 and 1 will be saved.

Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys will be executed before the first good guy.
Input
The input file consists of separate lines containing k. The last line in the input file contains 0. You can suppose that 0 < k < 14.
Output
The output file will consist of separate lines containing m corresponding to k in the input file.
Sample Input
3
4
0
Sample Output
5
30

一个队列,前K个人是好人,后K个人是坏人。用约瑟夫环规则来剔除队列里的人。问:先把所有坏人剔除(在此前不能剔除一个好人),最小的m是多少?

思路:
开始用数组模拟循环链表写,明显会超时,复杂度O(km^2)。但我还是抱着试一试的心态去运行了下。。。果断表都打不出。后来联想到约瑟夫环有O(n)的数学解法求出最后留下的人。那么这题也可以类似这么做。原理都一样,就是重标号。把剔除一个人后剩下的人看成一个新环。至于在新环里怎么找出好人这是解题的关键。我们可以设置两个指针,一个指向第一个好人,一个指向最后一个好人。两个指针之间肯定都是好人。如果将要剔除的下标在这两个指针内,那么肯定非法。那么现在的问题在于更新这两个指针。
两指针分别设为head和tail,即将出队的下标记为pop。pop=(m-1)%len(len表示当前环长度)。那么pop+1为新环的第一个人(标号0),pop+2为第二个人(标号1)。以此类推,我们便能把所有剩下的人重新标号。
让我们来看下当前标号为x的人,在重标号后的x’是多少。首先我们计算出他与pop+1的间距,显然式子是这样的:d=(x-(pop+1)%len+len)%len ((pop+1)%len是pop后一个人的下标),然后 x’=d%(len-1)。所以最后x’= ((x-(pop+1)%len+len)%len)%(len-1)。用这个式子我们便能更新head和tail了。
这样复杂度O(mk),1s内跑出来无压力。网络上大部分打表,实在没有必要。

#include<stdio.h>int f[15] = { 0 };int main(){    int head, tail, pop;    for (int k = 1; k <= 13; k++)    {        int m;        for ( m = k + 1;; m++)        {            int i;            for ( i = 2 * k, head = 0, tail = k - 1;i>k; i--)            {                pop = (m - 1) % i;                if (pop >= head&&pop <= tail) break;                head = ((head - (pop + 1) + i) % (i - 1))  ;                tail = ((tail - (pop + 1) + i) % (i - 1));            }            if (i == k)                break;        }        f[k] = m;    }    int k;    while (~scanf("%d",&k)&&k)    {        printf("%d\n", f[k]);    }    return 0;}