POJ 1012 Joseph 解题报告

来源:互联网 发布:专业网速测试软件 编辑:程序博客网 时间:2024/05/02 01:15

题目如下:
Joseph
Time Limit: 1000MS  Memory Limit: 10000K
Total Submissions: 28148  Accepted: 10512

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

Central Europe 1995

-------------------------------------------------------------------------------------------------------------------------

本题目由Joseph问题转化而来,选贴出两段用数学方法计算Joseph
问题的代码:

第一段代码是求最后留下来的人的编号,代码如下:
(具体可以参考:http://baike.baidu.com/view/717633.htm?fr=ala0_1#2)
/*****************************************

 Joseph Problem
 Method: Maths, Recurrence
 f[1] = 0
 f[i] = (f[i-1] + m) mod i, i > 1
 i: Number of People
 f[i]: Last Number

*****************************************/

如果是要求所有人出列的顺序,可以用这个:
//////////////////////////////////////////////////////
约瑟夫问题的数学解法:(n个人报数,从1开始,报到m出列)
e(i)=f(i)+1
其中:
f(1)=(m-1)%n
f(2)=((m-1)%(n-1)+m)%n
f(3)=(((m-1)%(n-2)+m)%(n-1)+m)%n
f(i)=g(i)
其中: 
i=2时:
g(1)=(m-1)%(n-i+1);
g(2)=(g(1)+m)%(n-i+2);
f(2)=g(2);
i=3时:
g(1)=(m-1)%(n-i+1);
g(2)=(g(1)+m)%(n-i+2);
g(3)=(g(2)+m)%(n-i+3);


*******************************************************
i=t时:
g(1)=(m-1)%(n-i+1);
g(t)=((g(t-1)+m)%(n-i+t))%(n-i+t)
    =(g(t-1)+m)%(n-i+t);
注:对于函数g(t)来讲,t为变量,i和f(i-1)均为常量。
*******************************************************

 

C++实现如下:
//实现n个人报数,从k开始,报到m的出列的C++程序如下:

好了,说这道题目吧,首先想到的就是用上面第二段代码对M的值(m=k+1.....)做循环判断,直到找到符合条件的m值为止。
代码如下:

提交一下,得到TLE,再看,发现问题在于重复计算了,浪费了好多时间,因为值只有1----13,所以至多只需要计算13次就可以了,
将计算结果保存下来,以后再输入的话,直接输出就可以了,改为以下的:

提交一下,终于AC,不过时间显示594MS,太多了,继续优化。。。
在网上看到一位达人说m%(k+1)结果为0或1,这是为什么呢?
原因如下:当最后剩下一个坏人和k个好人的时候,假如坏人的编号为t,则有:

1,2,3,4,......k,t    k+1<=t<=2k
可将其重新编号以便理解:1,2,3,4........k,k+1
下一个只能从t或1开始报数,
从1开始时,若要t出列,则:(1+m)%(k+1)==0;
从t开始时,若要t出列,则:((k+1)+m)%(k+1)==k+1=>m%(k+1)==0
从而得出:(m+1)%(k+1)=0或m%(k+1)=0

修改后的代码如下:

提交之后时间显示:219MS,不行还得优化,再改一下代码,这次只是处理上的优化,去掉了一些MOD运算:

这次是63MS,基本上满足要求了,暂时还没有发现更好的。。。。

过去一天,看别人的blog又有新的发现,因为题目比较特殊,前k个人的序号都是不会变化的,而且题目的要求只是满足条件的m,
没有要求求出出列的每个人在原来序列中的编号,所在可以进行一个转化:

我们假设每次坏人出列后我们都对序列重新编号并且在此之前m一直满足题目的条件,则有:

从刚出列的坏人(在上一序列的编号为t)后边的一个人(新的编号为t)重新报数,下一个出列的人“新的编号”位于[k+1....2k]中
<=>
从刚出列的坏人(在上一序列的编号为t)后边的一个人(新的编号为t)重新报数,下一个出列的人“在原始队列的编号”位于[k+1....2k]中

所以我们只需要进行前一个条件的判断就可以了,重写后的代码如下:

呵呵,仅用了16MS,此题基本上可以告一段落了。

 

 

 

原创粉丝点击