poj 1012 Joseph (约瑟夫问题)

Joseph
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 47657   Accepted: 17949

Description

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

Source

约瑟夫问题的变形,开始对题意就理解了好久,这类题目主要要找到中间的公式;这道题就要知道 当前出局的位置 = (前一个出局的位置+m-1)%(2k-(k-1);

是从0开始数,这就是一个通式;明白了这个公式这个题相当于就做出了一半;这中间还有一个判别的条件,就是如果当前的位置如果<k,说明这种情况就不满足了;

第一种方法:就直接可以对m的值进行枚举,利用判别条件,找出合适的m值;

下面是代码;这种思路好像200ms可以过;

//暴力+枚举
#include <cstdio>
#include <cstring>
int main()
{
    int k;
    int ans[20];//储存当前的位置
    int Joseph[20]={0}; //储存符合条件的m的值
    for(k=1;k<14;k++)
    {
        int m=1;
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=k;i++)
        {
            ans[i]=(ans[i-1]+m-1)%(2*k-i+1); //递推公式
            if(ans[i]<k)
            {
                i=0;
                m++;
            }
        }
        Joseph[k]=m;
    }
  while(scanf("%d",&k)&&k!=0)
   {
       printf("%d\n",Joseph[k]);
   }
   return 0;
}
要想0ms过的,直接得出结果数组,然后再提交;

//直接枚举 0ms
#include <cstdio>
#include <cstring>
int main()
{
    int k;
  int Joseph[]={0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,1245064};
  while(scanf("%d",&k)&&k!=0)
   {
       printf("%d\n",Joseph[k]);
   }
   return 0;
}
第二种方法,要挖掘题目中的隐含条件,我看了好久都没看出来,代码是参考别人的,这种46ms就可以过;优化了不少;

题目中的隐含条件;只剩下一个坏人的时候,下一个报数的人要么是第1个人,要么是第k+1个人;所以间隔就是m=s*(k+1)或者是 m=s*(k+1)+1;

粗略的证明一下,参考别人的:(这里我也还有点没懂)

仅剩一个坏人,圈长为k+1;设下一个报数人为第一个人时的时间间隔为m1;下一个报数人为第k+1个人时的时间间隔为m2;

由 (1+m1)%(k+1)=1 得出 m1= s*(k+1);

   (k+1+m2)%(k+1)=1  得出 m2=s*(k+1)+1;

这样我们就可以对m的枚举优化剪枝,m是(k+1)的倍数或者是他的倍数+1;

#include <cstdio>
#include <cstring>
bool Joseph(int k,int m)
{
    int n=2*k,x=0;
    while(n>k)
    {
        x=(x+m-1)%n;//递推公式,算出当前出局的位置
        if(x<k) //判别条件
            return false;
        n--;
    }
    return true;
}
int main()
{
    int k;
    int result[20]={0};
    for(k=1;k<14;k++)
    {
        for(int i=k+1;;i+=k+1) //对m枚举进行优化
        {
            if(Joseph(k,i)) //m是k+1的倍数或者倍数+1
            {
                result[k]=i;
                break;
            }
            else if(Joseph(k,i+1))
            {
                result[k]=i+1;
                break;
            }
        }
    }
    while(scanf("%d",&k)&&k)
    {
        printf("%d\n",result[k]);
    }
}


总结一下:对于这一类题型要读懂题目的题意,挖掘题目的隐含条件,多构思,理清思路才开始动手写。





你可能感兴趣的:(ACM,poj)