zoukankan      html  css  js  c++  java
  • HDU 3089 (快速约瑟夫环)

    Josephus again

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 652    Accepted Submission(s): 181


    Problem Description
    In our Jesephus game, we start with n people numbered 1 to n around a circle, and we eliminated every k remaining person until only one survives. For example, here's the starting configuration for n = 10, k = 2, The elimination order is 2, 4, 6, 8, 10, 3, 7, 1, 9. So 5 survives.The problem: determine the survivor's number , J(n, k).
     
    Input
    There are multiple cases, end with EOF
    each case have two integer, n, k. (1<= n <= 10^12, 1 <= k <= 1000)
     
    Output
    each case a line J(n, k)
     
    Sample Input
    10
    2
    10
    3
     
    Sample Output
    5
    4
     
    first try:
    #include "iostream"
    #include "cstdio"
    using namespace std;
    #define LL long long
    int main(){
        LL gg, n, k;
        while(~scanf("%lld%lld",&n,&k)){
            gg=0;
            for(LL i=2; i<=n; i++){
                gg=(gg+k)%i;
            }
            printf("%lld
    ",(LL)gg+1);
        }
        return 0;
    }

    Time Limit Exceeded

    second try:

    约瑟夫环,但是n超大

    快速约瑟夫环

    约瑟夫环递推公式,n为人数,k为步长。

    f(1)=0

    f(n)=[f(n-1)+k]%i  i∈[2,n]

    f(n)还要经过起始位置修正,设起始位置为s,即ans=[f(n)+s]%n。

    基本约瑟夫环优化就是当k=1的时候,每次递推就是在+1,可以直接算出来快速跳过,f(n)=f(1)+n-1

    当n超大的时候,可以照着这种思路快速简化递推过程。在递推后期,f(x)+k在很长的周期内<i,假设有m个周期,

    那么这些周期合并后的结果相当于f(x)+m*k。可以快速跳过。条件限制是: f(x)+m*k<i+(m-1)

    可以推出来:

    当m=1时,条件限制: f(x)+k<i

    当m=2是,条件限制: f(x+1)+k<i+1=f(x)+2*k<i+1

    当m=m时,条件限制:f(x)+m*k<i+(m-1)

    化简有m<(i-f(x)-1)/(k-1),若能整除,就是(i-f(x)-1)/(k-1)-1,否则就是(i-f(x)-1)/(k-1)直接取整。

    这样,i+=m,f(x)+=m*k,快速跳过了中间过程。

    若i+m>n,说明快速跳越界了,这时候可以直接算出f(n)=f(x)+(n-i-1)*m。

    #include "cstdio"
    #define LL long long
    LL solve(LL n,LL k,LL s=1)
    {
        if(k==1) return (n-1+s)%n;
        LL ans=0;
        //ans=(ans+k)%i
        for(LL i=2;i<=n;)
        {
            if(ans+k<i) //快速跳跃
            {
                LL leap;
                if((i-ans-1)%(k-1)==0) leap=(i-ans-1)/(k-1)-1;
                else leap=(i-ans-1)/(k-1);
                if(i+leap>n) return ((ans+(n+1-i)*k)+s)%n;
                i+=leap;
                ans+=leap*k;
            }
            else
            {
                ans=(ans+k)%i;
                i++;
            }
        }
        return (ans+s)%n;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        LL n,k;
        while(scanf("%I64d%I64d",&n,&k)!=EOF)
        {
            LL ans=solve(n,k);
            if(ans==0) printf("%I64d
    ",n);
            else printf("%I64d
    ",ans);
        }
    }

    感谢:http://www.cnblogs.com/neopenx/p/4505298.html

    算法性能考验!十分重要!

  • 相关阅读:
    回溯法---哈密顿回路(5)
    回溯法---n皇后问题(4)
    回溯法---n-着色问题(3)
    回溯法--算法框架(2)
    创建二叉树的所有深度上的节点链表
    笔试
    笔试 (2)
    LeetCode278-第一个错误的版本(二分查找)
    LeetCode46-全排列(递归)
    LeetCode258-各位相加(猜想公式)
  • 原文地址:https://www.cnblogs.com/kimsimple/p/7468417.html
Copyright © 2011-2022 走看看