1 条题解

  • 0
    @ 2025-5-22 19:33:37

    约瑟夫环问题变种解析

    这个问题是经典约瑟夫环问题的变种。在原版问题中,每数到第三个人就会被杀掉,而本题的变种是每数到第二个人离开。

    问题分析

    规则

    • n个人围成一圈,编号1到n
    • 从1号开始计数,每次数到第二个人就让其离开
    • 求最后剩下的人的编号

    示例:当n=5时,离开顺序为2→4→1→5,最后剩下3号。

    数学规律推导

    通过分析小规模案例,我们可以找到规律:

    • 当n=1时,存活者是1
    • 当n=2时,存活者是1
    • 当n=3时,存活者是3
    • 当n=4时,存活者是1
    • 当n=5时,存活者是3
    • 当n=6时,存活者是5
    • 当n=7时,存活者是7
    • 当n=8时,存活者是1

    观察发现,当n是2的幂时,存活者总是1。对于其他数值,存活者的位置可以通过公式计算:

    1. 找到小于等于n的最大2的幂,记为2^k
    2. 存活者位置 = 2*(n - 2^k) + 1

    代码实现解析

    #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') // 读取输入直到遇到00e0
        {
            n=s[0]-'0';  // 提取第一位数字
            m=s[1]-'0';  // 提取第二位数字
            z=s[3]-'0';  // 提取零的个数
            p=(n*10+m);  // 组成前两位数字
            for (int i=1;i<=z;i++)  // 添加z个零
                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++)  // 找到小于等于p的最大2的幂
                        k=i;
                    if (ef[k+1]==p)  // 如果p恰好是2的幂
                        cout<<1<<endl;
                    else
                    {
                        t=1+2*(p-ef[k]);  // 应用公式计算存活者位置
                        cout<<t<<endl;
                    }       
                }   
            }       
        }
        return 0;
    }
    

    关键步骤解释

    1. 输入处理

      • 输入格式为"xyez",表示数字n = xy后面跟z个零
      • 例如"05e0"表示5,"01e1"表示10
    2. 打表优化

      • 使用数组ef存储2的前30次幂,方便快速查找
    3. 数学公式应用

      • 找到小于等于n的最大2的幂2^k
      • 如果n恰好是2的幂,直接返回1
      • 否则,使用公式2*(n - 2^k) + 1计算存活者位置

    复杂度分析

    • 时间复杂度:每次查询为O(log n),主要用于查找小于等于n的最大2的幂
    • 空间复杂度:O(1),使用固定大小的数组存储2的幂

    这个算法通过数学规律直接计算结果,避免了模拟淘汰过程,高效解决了大规模数据下的问题。

    • 1

    信息

    ID
    782
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者