您的位置:首页 > 编程语言 > C语言/C++

POJ_1781_In Danger_约瑟夫问题

2015-10-31 21:06 344 查看
这道题是约瑟夫问题的变形,大意就是给定很多人,围成一个环,从一开始数,每次杀死第三人,事实上按数据是每数到2就杀死当前的人。第一个样例中,一共五人,先杀死2号,之后是4号,然后1号,最后杀死5号,于是3号最后活下来了。

输入数据,读为字符型较方便,我是直接读一个字符串s,然后计算人数人数,这个人数很多,模拟的话一定会超时,但是模拟的方法先简述一下:

设有n个人,数到m杀死当前人。

定义一个布尔型数组记录人的生死,初始化为true,代表都活着。定义一个k=0,记录已经杀死几个人。一个变量t记录当前人的编号,x记录数到了几。

当k不等于n时,t每次加一,若t大于n;则令t=1。若通过布尔型数组判断编号为t的人还活着,x也加一。若x==m,令数组下标为t的值为false。那么最后的t值即为活下来的人。

核心代码(模拟):

memset (a,true,sizeof(a));
s=0,t=0,k=0;
do
{
t++;
if (t>n)
t=1;
if (a[t]==true)
s++;
if (s==m)
{
a[t]=false;
s=0;
k++;
if(k==n)
cout<<t<<endl;
}
}while (k!=n);


然而对于这道题,需要找到一些规律,否则会超时。

那么假设n个人,每次杀死第m人时活下来的是编号为k的人,当m一定,对于n+1个人来说,第一个杀死的是编号为m的人,还剩下n人,那么这就和n个人的情况一样了,只是不是从第一个人开始数,而是从m+1开始。这样有n+1个人时,最终活下来的为第k+m号人。同理,n+2时,为k+2m号。

问题是k每次加m时,n只是加上1,可能会出现算出的活下来的人的编号大于n的情况,此时怎么办呢?显然,我们对算出的数对n取模即可。

这时对本题中m一直为2,对于n为2的整数次幂时,第一次转一圈杀死了所有的编号为偶数的人,然后又从1号开始,这时剩下n/2个人,人数还是2的整数次幂,不妨把这些人重新按顺序编号,又杀死了那些编号为偶数的一半人,依然回到1……最后显然回到n=2的情况,最终活下来的人为1号。所以,当n为2的整数次幂时,活下来的人是1号。

这样对于一个任意的n,若是2的整数次幂答案为1,。其他的只要找到不大于它的最大的2的整数次幂的数x,然后算出1+2(n-x)即为答案。那么关于这个值为什么必然小于下一个2的整数次幂数,下一个数为2x,n-x>x时,n>2x,不大于n的最大的2的整数次幂的数就是2x了。

AC代码

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
long long p;
int n,m,z;
string s;
long long ef[50]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456};
//数组打表记录2的整数次幂
int main()
{
while (cin>>s,s[0]-'0'+s[1]-'0')
{
n=s[0]-'0';
m=s[1]-'0';
z=s[3]-'0';
p=(n*10+m);
for (int i=1;i<=z;i++)
p*=10;
if (p==1||p==2||p==4)
{
cout<<1<<endl;
}
else
{
if (p==3)
cout<<3<<endl;
else
{
long long t;
int k;
for (int i=1;p>ef[i];i++)
k=i;
if (ef[k+1]==p)
cout<<1<<endl;
else
{
t=1+2*(p-ef[k]);
cout<<t<<endl;
}
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj 约瑟夫环 c++ 题解