zoukankan      html  css  js  c++  java
  • 2018icpc沈阳-K.Let the Flames Begin (约瑟夫环问题)

    题意:

    转化为经典约瑟夫环问题:

    N个人围成一圈,从第一个开始报数,第K个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。

    然后现在给出 N(标号1~N)个人,每隔K个将要被杀掉,问第 M 个被杀掉的 标号是多少。(与顺时针或者逆时针无关)。

    思路:

    基础递推原理可以参考下

    博客:https://www.cnblogs.com/jjscm/p/4463555.html

    2019.11.28:发现之前自己当时写的有很多问题没有弄清楚

    首先约瑟夫环的递推公式(可以模拟一下递归,先到只剩下最后一个人,然后每一次往前一个状态回递推,求得这个人所在位置–注意这个递推公式求得是当前状态序列的位置下标

    即约瑟夫环通过递推,从最后的状态得到最初的状态。而由于要问第M个被杀掉的是谁,所以我们的初始状态不是从只剩下最后一人开始(长度为1),而是从(n-m+1)状态(还剩下n-m+1个人)开始

    而根据约瑟夫环递推式:(表示N个人,第M个出队的人位置) f[n][m] = (f[n-1][m-1] + k)%n; 所以转换后为 f[n-m+i] = (f[n-m+i-1] + k) % (n - m + i);

    由于题中数据大小为:1 ≤ n, m, k ≤ 1018,以及min{m,k} 总和不超过 2 × 106,m >= n.

    所以对于 m 较大的数(会发现模数大部分情况下远大于kk,即要移动 x次(x很大),才会走完一圈),也就是说可以用乘法代替多次加法。

    f[a][b] = ans , f[a+x][b+x] = ans + k *x; 而进行取模的条件为:ans + k*x >= a+x (移动长度大于剩下的人的个数)即 x >= (a - ans) / (k-1) ;

    然后剩余对 K = 1的特判情况,直接是 M 出队。

    于时转换为代码如下:

    code:

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    int main() {
        int cas = 1;
        int t;
        scanf("%d",&t);
        while(t--) {
            ll n,m,k;
            ll f;
            scanf("%lld%lld%lld",&n,&m,&k);
            printf("Case #%d: ",cas++);
            if(m <= k) {
                f = k % (n - m + 1);
                if(f == 0) f = n - m + 1;
                for(ll i = 2; i <= m; i++)
                    f = (f + k) % (n - m + i);
                printf("%lld
    ",f);
            } else {
                if(k == 1) printf("%lld
    ",m);
                else {
                    ll a = n - m + 1, b = 1;
                    ll f = k % a, x = 0;
                    if(f == 0) f = a;
                    while(b + x <= m) {
                        a += x, b += x, f += k * x;
                        f %= a;
                        if(f == 0) f = a;
                        x = (a - f) / (k - 1) + 1;
                    }
                    f += (m - b) * k;
                    f %= n;
                    if(f == 0) f = n;
                    printf("%lld
    ",f);
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    (转)vim重复命令
    (转)Linux 目录结构
    (转)Linux 文件权限
    (转)Linux查看用户及其权限管理
    linux banner命令
    redmine和svn server的部署
    OpenCV学习 7:图像形态学:腐蚀、膨胀
    OpenCV学习 6:平滑滤波器 cvSmooth()——2
    《将博客搬至CSDN》
    OpenCV学习 5:关于平滑滤波器 cvSmooth()函数
  • 原文地址:https://www.cnblogs.com/Tianwell/p/11548296.html
Copyright © 2011-2022 走看看