码迷,mamicode.com
首页 > 其他好文 > 详细

约瑟夫问题的变种 LA3882

时间:2016-04-19 21:38:49      阅读:274      评论:0      收藏:0      [点我收藏+]

标签:

题目大意:

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) +1)% n (注意可能小于0 ,这时候要加上n)

 

#include<iostream>
#define Size 1000005
using namespace std;

int n,m,k;
int f[Size];

int main(){
    cin>>n>>k>>m;
    
    f[1]=0;
    for(int i=2;i<=n;i++){
        f[i]=(f[i-1]+k)%i;
    }
    
    int ans=(m-k+1+f[n])%n;
    if(ans<1)ans+=n;
    cout<<ans;
    
    return 0;
} 

 

约瑟夫问题的变种 LA3882

标签:

原文地址:http://www.cnblogs.com/FuTaimeng/p/5410212.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!