约瑟夫问题

经典的约瑟夫问题,求最后一个剩下人的编号?

题目很多,这里给出一个:
http://hihocoder.com/problemset/problem/1296?sid=861197
递归公式:

f[1] = 0
f[n] = (f[n - 1] + K) mod n

1.当只有1个候选人时,最后一个剩下来的编号为0,毫无疑问的。

2.当有n-1个候选人时,则假设已经算出它最后的一个剩余的编号为s,即f[n]=s则有n个候选人是剩余的编号则为f[n] = (f[n - 1] + K) mod n

一个例子:

假设我们已经求解出了f[n - 1],并且保证f[n - 1]的值是正确的。
现在先将n个人按照编号进行排序:
0 1 2 3 … n-1
那么第一次被淘汰的人编号一定是K-1(假设K < n,若K > n则为(K-1) mod n)。将被选中的人标记为”#”:
0 1 2 3 … K-2 # K K+1 K+2 … n-1
第二轮报数时,起点为K这个候选人。并且只剩下n-1个选手。假如此时把k看作0’,k+1看作1’…
则对应有:

 0     1 2 3 ... K-2  # K  K+1 K+2 ... n-1
n-K'             n-2'   0'  1'  2' ... n-K-1'

此时在0’,1’,…,n-2’上再进行一次K报数的选择。而f[n-1]的值已经求得,因此我们可以直接求得当选者的编号s’。
但是,该编号s’是在n-1个候选人报数时的编号,并不等于n个人时的编号,所以我们还需要将s’转换为对应的s。
通过观察,s和s’编号相对偏移了K,又因为是在环中,因此得到s = (s'+K) mod n
f[n] = (f[n-1] + k) mod n

上述过程的时间复杂度为O(N),空间复杂度为O(1)

#include <iostream>
using namespace std;
int lastNum(int n,int k) {
    if (n==1) return 0;
    if (n<k) {
        int res=0;
        for (int i=2;i<=n;i++)
            res=(res+k)%i;
        return res;
    }
    int res=lastNum(n-n/k,k);
    return res<n%k ? res-n%k+n : res-n%k+(res-n%k)/(k-1);
}
int main() {
    int num;cin>>num;
    int n,k;
    while (num--) {
        cin>>n>>k;
        cout<<lastNum(n,k)<<endl;
    }
    return 0;
}
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值