问题 E: Josephus问题(Ⅲ)
题目描述
n个人排成一圈,按顺时针方向依次编号1,2,3…n。从编号为1的人开始顺时针"一二三…"报数,报到m的人退出圈子。这样不断循环下去,圈子里的人将不断减少。最终一定会剩下一个人。试问最后剩下的人的编号。
本题的数据规模更具有挑战性,尝试更通用且高效的算法。
输入
不超过1000组数据。
每组数据一行,每行两个正整数,代表人数n (1 <= n < 231)和m(1<=m<=1000)。
输出
每组输入数据输出一行, 仅包含一个整数,代表最后剩下的人的编号。
样例输入 Copy
7 2
2 2
样例输出 Copy
7
1
计算过程
与问题D相同,运用数学规律计算结果,但由题可知,此题数据规模远大于第二问,所以需要通过一定方法简化运算过程
这里小编使用的方法是通过判断数据大小所在范围,进行分段计算
1.报数为1时无需计算
2.报数大于1,人数大于2
这段代码是一个C++程序,它使用 bits/stdc++.h
头文件来包含标准库,使用 using namespace std;
来避免重复使用 std::
前缀。程序的主要目的是解决一个特定的数学问题,通过循环读取输入直到输入结束(EOF),然后根据输入计算并输出答案。
以下是代码的逐步解析:
-
变量声明:
long long num
和ans
用于存储输入的两个长整型数值。long long a
用于存储中间计算的结果。long long p, q
用于循环中的计算。
-
输入循环:
- 使用
scanf
函数读取ans
和num
,直到遇到文件结束符 EOF。
- 使用
-
特殊情况处理:
- 如果
num
等于 1,直接输出ans
并继续下一次循环。
- 如果
-
一般情况处理:
- 如果
num
不等于 1,执行以下循环和逻辑:- 初始化
p
为 2,a
为 0。 - 使用
for
循环,从p
等于 2 开始,直到p
大于ans
。 - 在循环中,首先检查
a + num
是否小于p
:- 如果是,计算
q
的值,它是(p - 1 - a) / (num - 1)
的整数部分,如果(p - 1 - a)
是(num - 1)
的倍数,则减去 1。 - 如果
p + q
大于ans
,则计算a
的新值并退出循环。 - 如果不是,更新
p
和a
的值,继续循环。
- 如果是,计算
- 如果
a + num
不小于p
,则更新a
的值,增加p
。
- 初始化
- 如果
-
输出结果:
- 在循环结束后,输出
a + 1
作为最终答案。
- 在循环结束后,输出
-
程序结束:
- 返回 0,表示程序正常结束。
代码逻辑分析:
- 这段代码是在解决一个关于数字序列的问题,其中
num
可能代表某种操作或增量,ans
代表序列的最终大小或目标值。 - 循环中的逻辑涉及到对序列的增长和缩减的模拟,以及在特定条件下的退出。
- 代码中的注释提供了一些线索,但具体的数学问题背景和逻辑需要更多的上下文来完全理解。
潜在问题:
- 代码中的注释和变量命名对于理解程序逻辑至关重要,但当前的注释可能不足以完全理解程序的意图。
- 代码的可读性和维护性可以通过改进变量命名和添加更详细的注释来提高。
改进建议:
- 使用更具描述性的变量名,以提高代码的可读性。
- 添加更详细的注释,解释每个部分的逻辑和数学原理。
- 考虑使用现代C++输入/输出库,如
iostream
和cin
/cout
,以提高代码的可移植性和安全性。
部分实现
1.引入库
代码如下(报数为1时无需计算):
if(num==1){
cout<<ans<<endl;
continue;
}
代码如下(报数大于1且人数大于2):
else{
for(p=2;p<=ans;){
if(a+num<p){
//当a+num<p时可以减少时间复杂度
if((p-1-a)%(num-1)==0){
//q不能是num-1的倍数
q=(p-1-a)/(num-1)-1;
}
else q=(p-1-a)/(num-1);
//a+num*q<p+q-1变换而来
if(p+q>ans){
//p+q>ans ---->p+q-1==ans---->q=ans-(p-1)
a=(a+(ans-(p-1))*num)%ans;
break;
//当还有ans个人时的情况已找到,退出循环
}
p=p+q;
a=(a+q*num)%p;
}
else{
a=(a+num)%p;
p++;
}
}
}
AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
long long num,ans;
while(scanf("%lld%lld",&ans,&num)!=EOF){
long long a=0,p,q;
//1.num=1
if(num==1){
cout<<ans<<endl;
continue;
}
//2.num≠1判断a+num与p的大小关系并根据上述规律以及应当所剩人数计算答案所处位置
else{
for(p=2;p<=ans;){
if(a+num<p){
//当a+num<p时可以减少时间复杂度
if((p-1-a)%(num-1)==0){
//q不能是num-1的倍数
q=(p-1-a)/(num-1)-1;
}
else q=(p-1-a)/(num-1);
//a+num*q<p+q-1变换而来
if(p+q>ans){
//p+q>ans ---->p+q-1==ans---->q=ans-(p-1)
a=(a+(ans-(p-1))*num)%ans;
break;
//当还有ans个人时的情况已找到,退出循环
}
p=p+q;
a=(a+q*num)%p;
}
else{
a=(a+num)%p;
p++;
}
}
}
cout<<a+1<<endl;
}
return 0;
}