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);文章来源地址https://www.yii666.com/article/754343.html网址:yii666.com<

是从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;

粗略的证明一下。參考别人的:(这里我也还有点没懂)文章来源地址:https://www.yii666.com/article/754343.html

仅剩一个坏人,圈长为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;文章地址https://www.yii666.com/article/754343.html

#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]);
}
}

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







网址:yii666.com

版权声明:本文内容来源于网络,版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。文本页已经标记具体来源原文地址,请点击原文查看来源网址,站内文章以及资源内容站长不承诺其正确性,如侵犯了您的权益,请联系站长如有侵权请联系站长,将立刻删除

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信图片_20190322181744_03.jpg

微信扫一扫打赏

请作者喝杯咖啡吧~

支付宝扫一扫领取红包,优惠每天领

二维码1

zhifubaohongbao.png

二维码2

zhifubaohongbao2.png