POJ 1012 Joseph题解与心得

POJ 1012 Joseph题解与心得

2010-06-06

这几天都在收集一些DP的资料，但读了一些入门的资料后我发现DP比我想象中的还要难入门，主要是DP问题各概念都比较抽象，想要一口把它的概念吃透有着不小难度，心急的朋友不要在这里踩进深坑了。经过几天思考，还没有摸到边缘的我打算先将DP放下，不然自己的信心越来越受打击了，为了不让自己对ACM的激情出现丝毫的减弱并且不陷进自卑的恶性循环当中，我决定选一些比较简单而且有趣的题目练练手，增加信心。

POJ 1012 Joseph问题，当我看到这个问题的描述时，我就被这个问题的趣味性深深吸引，于是便动起了首手……

题目的描述是这样的：

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.

n个人，分别编号为1,2,3,n，让他们围成一个圈站着，从指定的第一个人开始数起，每次第m个人被处死，然后在剩下的人中重复数数，只有最后一个剩下的人才能活下来。例如:n=6m=5时，这些人当中被处死的顺序是：5,4,6,2,3，最后1活下来了。

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

我当时的第一想法便是把圈化直线，通过让一个位置i不断累加指定计数值m，然后计算出与当前总人数n的倍数的差值hc*n<=i），当h<=k（输入数）时说明该m不满足第一个好人被处死前所有坏人都被处死的条件，所以m加一（m++）跳到下一个计数值重新开始，当h>k时，即处死的是坏人时，继续下一轮对i累加m，而循环k次均满足h>k条件时，该m就为所求。输出该m

程序如下：

#include <stdio.h>

int main()

{

long i;

int k, n, m, flag, count;

while(scanf("%d", &k)==1)

{

if(k==0)

break;

for(m=k+1; ; m++)

{

n = 2*k;

flag=0;

i=0;

count=1;

while(n>k)

{

i+=m;

if(i%n>k || i%n==0)

{

i-=count;

n--;

count++;

}

else

{

flag=1;

break;

}

}

if(flag==0)

{

printf("%d/n", m);

break;

}

}

}

return 0;

}

n=6m=5时，

#include <stdio.h>

int main()

{

long i;

int k, n, m, flag, count;

while(scanf("%d", &k)==1)

{

if(k==0)

break;

for(m=k+1; ; m++)

{

n = 2*k;

flag=0;

i=0;

while(n>k)

{

i+=m;

if(i%n>k || i%n==0)

{

count=i/n;

if(i%n==0)

count--;

i-=(count+1);

n--;

}

else

{

flag=1;

break;

}

}

if(flag==0)

{

printf("%d/n", m);

break;

}

}

}

return 0;

}

经过改进后的程序经调试可以得到正确结果。

#include <stdio.h>

int main()

{

int k, n, m, flag, i;

while(scanf("%d", &k)==1)

{

if(k==0)

break;

for(m=k+1; ; m++)

{

n = 2*k;

flag=0;

i=0;

count=1;

while(n>k)

{

i+=m;

i=i%n;

if(i>k || i==0)

{

if(i==0)

i=n-1;

else

i--;

n--;

}

else

{

flag=1;

break;

}

}

if(flag==0)

{

printf("%d/n", m);

break;

}

}

}

return 0;

}

虽然解题过程是正确的，得到答案也是正确的，但是提交的时候超时了，这个程序还能优化吗？我苦思了一段时间，未果，于是我到网上去搜了搜别人的题解，他们的解法思路与我的思路大致相同，但有一点不同的地方就是他们大多通过打表实现，并且我发现了一个神奇得令我神往的短小精干版本。

#include <stdio.h>

int a[15]={0, 2, 7, 5, 30, 169, 441, 1872, 7632, 1740, 93313, 459901, 1358657, 2504881, 13482720};

int main()

{

int k;

while(scanf("%d", &k)==1)

{

if(k==0)

break;

printf("%d/n", a[k]);

}

return 0;

}

#include <stdio.h>

int a[15];

int main()

{

long i;

int k, n, m, flag, count, j;

for(j=1; j<=14; j++)

{

for(m=j+1; ; m++)

{

n = 2*j;

flag=0;

i=0;

count=1;

while(n>j)

{

i+=m;

i=i%n;

if(i>j || i==0)

{

if(i==0)

i=n-1;

else

i--;

n--;

}

else

{

flag=1;

break;

}

}

if(flag==0)

break;

}

a[j] = m;

}

while(scanf("%d", &k)==1)

{

if(k==0)

break;

printf("%d/n", a[k]);

}

return 0;

}

• 本文已收录于以下专栏：

举报原因： 您举报文章：POJ 1012 Joseph题解与心得 色情 政治 抄袭 广告 招聘 骂人 其他 (最多只允许输入30个字)