Josephus问题(Ⅲ)

题目描述

n个人排成一圈,按顺时针方向依次编号1,2,3…n。从编号为1的人开始顺时针"一二三...."报数,报到m的人退出圈子。这样不断循环

下去,圈子里的人将不断减少。最终一定会剩下一个人。试问最后剩下的人的编号。


本题的数据规模更具有挑战性,尝试更通用且高效的算法。

输入

 超过1000组数据。

每组数据一行,每行两个正整数,代表人数n (1 <= n < 231)和m(1<=m<=100)。

输出

每组输入数据输出一行, 仅包含一个整数,代表最后剩下的人的编号。

样例输入

7 2
2 2

样例输出

7
1
思路:按照约瑟夫问题(I )中的解法二的式子进行分析
发现若ans+m小于i时,ans=(ans+m)%i;此式的ans是递增的,故可以直接加上m直到ans是小于i的最大整数。不要小看了这个式子,因为此题中的n范围是
1——2^31,m的范围是1——100,故对于大多数数据n都是大于m的,所以这个式子可以很大程度上减小时间复杂度。
根据上面的分析设x为增加的次数,列式ans+m*x-1
i+x赋给i时可能会大于n,这是就可以结束递增了,ans+=(n+1-i)*m;然后运行 else 部分


#include
#include
using namespace std;
typedef long long LL;
LL solve(LL n,LL m,LL s)
{
    if(m==1) return (n-1+s)%n;
    LL ans=0,i=2;
    while(i<=n)
    {
        if(ans+mn)
            {
                ans+=(n+1-i)*m;
                break;
            }
            i+=x;
            ans+=x*m;
        }
        else  ///ans不是递增时
        {
            ans=(ans+m)%i;///普通求法
            i++;
        }
    }
    return (ans+s)%n;
}
int main()
{
    LL n,m;
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
        LL ans=solve(n,m,1);
        printf("%lld\n",ans?ans:n);
    }
    return 0;
}


 

你可能感兴趣的:(ACM_数学)