• LA 3882 And Then There Was One


    解题思路:分析要好久,懒得分析了,贴了某大牛的的分析,代码就是我自己写的。

         

    N个数排成一圈,第一次删除m,以后每k个数删除一次,求最后一被删除的数。

    如果这题用链表或者数组模拟整个过程的话,时间复杂度都将高达O(nk),而n<=10000,k<=10000 目测会直接TLE。

    那么有没有其他的方法呢?答案是有的。

    我们先忽略掉m, 分析一下每k个数删除一次,那就是经典的约瑟夫问题了。

    那么,将每个数(1~n)按顺序编号为0~n-1

    设第一个删除的数的编号为x,则x= k %n-1 (注意是编号,真正删除的数为编号+1)

    那么剩下的n-1个数可以组成一个新的约瑟夫环。

    现在的编号是什么呢?显然:(令x+1=y ,就是说y= k%n)

    y ,  y+1 , y+2  ...  n-1  , 0 , 1  ... y-2

    把y放在第一个的目的是下一次从它开始数数。

    重新开始数k个数.

    你说重新?嗯。那么就可以这样重新编号:

    y             -> 0

    y+1            ->1

    y+2            ->2

     ... 

     ... 

     y-2          -> n-2

    现在就变成了n-1个数(编号从0~n-2)的约瑟夫问题了!

    假设z是最后n-1个数留下的编号,那么z’是n个人留下的编号,则显然z’=(z+y)% n

    如何知道n-1个的解?往下递归就好了嘛,知道n-2即可

    所以,有:

    ans [1]=0;

    ans [n] =(ans[n-1]+k) %n;

    (可能有人要问了:上面不是z’=(z+y)% n吗?现在怎么变成 k了?因为y= k%n,模运算)

    然后,答案要+1 (编号->数)

    那么这一题第一次是m怎么办呢?

    也很简单,我们每次都移动K ,有n个数,那么答案就是ans[n]

    但是第一次移动的是m,所以后面的移动都有个恒定的差距(k-m)

    所以答案为:(ans[n] – (k – m) )% n (注意可能小于0 ,还有最终答案+1)

     1 #include<cstdio>
     2 int main()
     3 {
     4     int n, k, m, A[10005];
     5     while(~scanf("%d%d%d", &n, &k, &m) && (n || m || k))
     6     {
     7         A[1] = 0;
     8         for(int i = 2; i <= n; i++) A[i] = (A[i-1]+k)%i;
     9         int a = (m - k + 1 + A[n]) % n;
    10         if(a <= 0) a += n; //注意可能小于0
    11         printf("%d
    ", a);
    12     }
    13     return 0;
    14 }
    View Code

     

  • 相关阅读:
    ASP.NET Web Form(八)
    ASP.NET Web Form(八)
    软考知识点总结
    软考知识点总结
    c# 引用外部dll
    c# 引用外部dll
    ORA600 [Kcbz_check_objd_typ_1] Running a Job (Doc ID 785899.1)
    oracle创建em
    1918: 等值数目
    1917: 支配值数目
  • 原文地址:https://www.cnblogs.com/loveprincess/p/4834736.html
Copyright © 2020-2023  润新知