1 条题解
-
0
约瑟夫环问题变种解析
这个问题是经典约瑟夫环问题的变种。在原版问题中,每数到第三个人就会被杀掉,而本题的变种是每数到第二个人离开。
问题分析
规则:
- 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。对于其他数值,存活者的位置可以通过公式计算:
- 找到小于等于n的最大2的幂,记为2^k
- 存活者位置 = 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; }
关键步骤解释
-
输入处理:
- 输入格式为"xyez",表示数字n = xy后面跟z个零
- 例如"05e0"表示5,"01e1"表示10
-
打表优化:
- 使用数组
ef
存储2的前30次幂,方便快速查找
- 使用数组
-
数学公式应用:
- 找到小于等于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
- 上传者