POJ_1781_In Danger_约瑟夫问题

本文探讨了一种约瑟夫问题的变形,并提出了一种高效算法来解决该问题。通过对问题模式的观察,发现了一个关键规律,即当人数为2的幂次时,存活者总是第一个人。对于其他情况,可以通过找到最接近且小于当前人数的2的幂次,利用公式计算出存活者的编号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题是约瑟夫问题的变形,大意就是给定很多人,围成一个环,从一开始数,每次杀死第三人,事实上按数据是每数到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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值