zoukankan      html  css  js  c++  java
  • uva3882

    约瑟夫问题的变形

    看了网上的题解,这题有一个trick就是每轮删完点后将剩下的点重新编号,从0编到n-1。

    这样操作之后,我们就可以对删点前后的两个状态进行递推了。

    假设我现在有一排点,编号为0到n-1,现在我从0开始数k个删掉第k个点,也就是删掉点k-1。

    剩下的点为0,1,2……k-2,k……,n-1

    我从k这个点开始重新编号,即:

    这样子问题具有相似性了。(因为同样是从0开始数到第k个删掉)

    那么怎么递推呢?

    可以看出,想要恢复上一轮的编号,只需要+k再整体%n即可

    如果记f[n] = n个数从0开始编号数到第k个删掉最后剩下的数的编号的话,

    f[n]  = (f[n-1] + k) % n;(注意这里的n是变化的)

    最后输出(m-k+1+f[n])%n。

    这个式子拆成两部分来解释,一部分是+1:

    因为我们设计的状态是从0开始的,然而题目是从1开始的,所以最后加1。

    另一部分是m-k:

    这是因为要删掉第m个,然而我们设计的状态是从0开始数k个删,这样如果我们加上m-k的话,就相当于成功地把m当成了第一个删的对象。(我们之前第一个删的对象是k嘛)(很难表达。。。见谅)

    综上输出(m-k+1+f[n])%n

    最后如果答案小于0,还要加上n。

    #include <cstdio>
    
    using namespace std;
    
    const int maxn = 10005;
    
    int n, k, m;
    
    int f[maxn];
    
    int main()
    {
        while(scanf("%d%d%d", &n, &k, &m) && n)
        {
            f[1] = 0;
            for (int i = 2; i <= n; i++)
                f[i] = (f[i - 1] + k) % i;
            int ans = (f[n] + m - k + 1) % n;
            if (ans <= 0) ans += n;
            printf("%d
    ", ans);
        }
        return 0;
    } 

    代码很简单,思路很复杂。

  • 相关阅读:
    平时十六测
    平时十五测
    平时十四测
    poj 1486 Sorting Slides
    POJ 3621Sightseeing Cows
    POJ 2728 Desert King
    POJ3111 K Best
    NOIopenjudge 407:Heritage
    POJ#2065. SETI
    HDU 4607 Park Visit
  • 原文地址:https://www.cnblogs.com/yohanlong/p/7754601.html
Copyright © 2011-2022 走看看