问题 D: Josephus问题(Ⅱ)
题目描述
n个人排成一圈,按顺时针方向依次编号1,2,3…n。从编号为1的人开始顺时针"一二"报数,报到2的人退出圈子。这样不断循环下去,圈子里的人将不断减少。最终一定会剩下一个人。试问最后剩下的人的编号。
n很大,直接模拟题意不行,请找出规律。
输入
不超过1000组数据。
每组数据一行,每行一个正整数,代表人数n。 (1 <= n < 231)
输出
每组输入数据输出一行, 仅包含一个整数,代表最后剩下的人的编号。
样例输入
7
2
样例输出
7
1
计算过程
1.通过暴力破解得出以下代码,但当n过大时会运算超时
#include <stdio.h>
#include <stdlib.h>
#define max 1000
int main()
{
int n;
while( scanf("%d",&n) != EOF )
{
int a[max],i;
for( i = 0; i < n; i++ )
a[i] = i + 1; //创建数组,且数组长度为len
int t = 1,k = 0;
while( k != n - 1 ) //用k来重复遍历数组
{
for( i = 0; i < n ; i++ )
{
if( a[i] != 0 )
{
if( t % 2 == 0 )
{
a[i] = 0;
k++;
}
t++;
}
}
}
for( i = 0; i < n ; i++ )
{
if( a[i] != 0 ) printf("%d\n",a[i]);
}
}
return 0;
}
2.简化运算过程
由以上代码输入1到15可得1 1 3 1 3 5 7 9 1 3 5 7 9 11 13 15
由以上结果可得出如下递推规律:所留下的编号均为奇数,且以1,1 3,1 3 5 7,1 3 5 7 9 11 13 15…出现,每次捆绑出现的个数均为2的次方。
根据等比数列的前n项和可得输入n所对应答案在哪个捆绑区域内,如:输入13,则有2^3 -1 < 13,所以13对应答案在1 3 5 7 9 11 13 15这一捆绑区域内;
根据等差数列(以13为例,这里我选择以15为首项,-2为公差),就可已得知13对应答案为数列中第(15-13+1)=3项,即为11。
下面是对代码的详细解析:
-
头文件和命名空间:
- 包含
<bits/stdc++.h>
头文件,这个头文件是GCC编译器的一个特性,它包含了几乎所有标准库的头文件。 - 使用
using namespace std;
来避免在标准库类型和函数前加std::
。
- 包含
-
辅助函数
found
:- 这个函数接受一个长整型参数
num
。 - 函数的目的是找到最小的
ans
,使得2^ans
大于或等于num
。 - 使用一个
for
循环,循环体内部没有执行任何操作,只是不断地增加ans
的值,直到pow(2, ans)
大于或等于num
。 - 循环结束后返回
ans
的值。
- 这个函数接受一个长整型参数
-
主函数
main
:- 从标准输入读取长整型变量
n
,直到输入结束(EOF)。 - 使用
found
函数计算++n
(即n+1
)对应的结果范围,存储在变量x
中。 - 计算等比数列的首项
a1
,它是2^x
。 - 计算
a2
,它是a1 - n + 1
,即首项减去n
加上 1。 - 计算
yue
(可能代表余数或某种特定的计算结果),它是a1 + 1 - 2 * a2
。
- 从标准输入读取长整型变量
-
输出结果:
- 使用
printf
函数输出yue
的值,然后换行。
- 使用
-
程序结束:
- 返回 0,表示程序正常结束。
代码逻辑分析:
- 代码中的
found
函数用于确定一个数属于哪个2的幂次范围内。 - 在
main
函数中,首先将输入的n
增加1,然后通过found
函数找到这个增加后的数所在的2的幂次范围。 - 然后,根据这个范围计算出
n
在等比数列中的准确位置,这似乎是通过某种特定的数学规律或公式来实现的。
潜在问题:
- 代码使用了
pow
函数,这是一个计算幂的函数,但它可能不是最高效的实现方式,特别是当num
非常大时。
改进建议:
- 为变量和函数提供更详细的注释,以帮助理解代码的逻辑和目的。
- 考虑使用更高效的算法或方法来替代
pow
函数,特别是当处理大数时。 - 考虑使用
iostream
库和cin
/cout
进行输入输出操作,以提高代码的可读性和可移植性。
部分实现
代码如下(求结果所在数字区域):
//通过规律计算等比数列结果,得到n应当被捆绑的数字区域
long long found( long long num )
{
long long ans;
for( ans = 0; pow( 2 ,ans ) < num; ans++ );
return ans;
}
代码如下(求结果):
//通过等差数列计算规律计算n在对应范围内的准确位置
long long a1 = pow( 2 , x ), a2 ,yue;
a2 = a1 - n + 1;
yue = a1 + 1 - 2 * a2;
AC代码
#include<bits/stdc++.h>
using namespace std;
//通过以上规律计算等比数列结果,得到n应当被捆绑的数字区域
long long found( long long num )
{
long long ans;
for( ans = 0; pow( 2 ,ans ) < num; ans++ );
return ans;
}
int main()
{
long long n;
while( scanf("%lld",&n) != EOF )
{
long long x = found( ++n ); //求得n对应结果的范围
//通过以上等差数列计算规律计算n在对应范围内的准确位置
long long a1 = pow( 2 , x ), a2 ,yue;
a2 = a1 - n + 1;
yue = a1 + 1 - 2 * a2;
printf("%lld\n",yue);
}
return 0;
}