问题 E: Josephus问题(Ⅲ)

问题 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),然后根据输入计算并输出答案。

以下是代码的逐步解析:

  1. 变量声明

    • long long numans 用于存储输入的两个长整型数值。
    • long long a 用于存储中间计算的结果。
    • long long p, q 用于循环中的计算。
  2. 输入循环

    • 使用 scanf 函数读取 ansnum,直到遇到文件结束符 EOF。
  3. 特殊情况处理

    • 如果 num 等于 1,直接输出 ans 并继续下一次循环。
  4. 一般情况处理

    • 如果 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 的新值并退出循环。
        • 如果不是,更新 pa 的值,继续循环。
      • 如果 a + num 不小于 p,则更新 a 的值,增加 p
  5. 输出结果

    • 在循环结束后,输出 a + 1 作为最终答案。
  6. 程序结束

    • 返回 0,表示程序正常结束。

代码逻辑分析

  • 这段代码是在解决一个关于数字序列的问题,其中 num 可能代表某种操作或增量,ans 代表序列的最终大小或目标值。
  • 循环中的逻辑涉及到对序列的增长和缩减的模拟,以及在特定条件下的退出。
  • 代码中的注释提供了一些线索,但具体的数学问题背景和逻辑需要更多的上下文来完全理解。

潜在问题

  • 代码中的注释和变量命名对于理解程序逻辑至关重要,但当前的注释可能不足以完全理解程序的意图。
  • 代码的可读性和维护性可以通过改进变量命名和添加更详细的注释来提高。

改进建议

  • 使用更具描述性的变量名,以提高代码的可读性。
  • 添加更详细的注释,解释每个部分的逻辑和数学原理。
  • 考虑使用现代C++输入/输出库,如 iostreamcin/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;
}   
 
  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值