约瑟夫环笔记+加速优化板子(HDU3089)

12 篇文章 0 订阅
12 篇文章 0 订阅
今天看到一题和约瑟夫环相关的问题,就把约瑟夫环的板子整了一下。
问题描述:n个人围成圈做,编号从0开始到n-1,从第0个人开始报数(从1开始),每次报到m的人出局,然后从后一个人开始继续往后报数。问最后获胜的人是谁。
思路:如果下标从0开始,则第一轮m-1被删除。剩下的数就又生成了一个大小为n-1的约瑟夫环。其中对应关系(旧–新)

k—-0
k+1—-1
k+2—-2
……
n-1—-n-k-1
0—-n-k
1—-n-k+1
……
k-3—-n-3
k-2—-n-2

如果说 f(n) 是n个数的约瑟夫问题的答案
那么可以得到递推式子
f(n)=(f(n1)+m)modn
到此,我们已经有了一个 O(n) 的求法了。一下是板子
long long Josephus(long long n, long long m, long long start = 0) {
    long long ans = 0;
    for(long long i = 2; i <= n; i++) {
        ans = (ans + m) % i;
    }
    return (ans + start) % n;
}
但是问题来了,如果n很大呢,如hdu3089,其中n为10的12次,但是m很小只有1000。
分析:当每次加m的时候其实很多时候ans+m并没有比i大,所以这里可以一次性加好多次,但是每次的i不一样,但是满足
ans+km<i+k1=>k(m1)<ians1
然后判断一下就好了,上代码,要注意题目中的下标
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<time.h>
#include<set>
#include<stack>
#include<vector>
#include<map>

#define pi acos(-1)
#define maxn 111111
#define maxm 11111
#define INF 0x3F3F3F3F
#define eps 1e-8

#define pb push_back

#define mem(a) memset(a,0,sizeof a)

using namespace std;

const long long mod = 1000000007;
/**lyc**/

void init(void) {

}
/**0到n-1下标**/
long long Josephus(long long n, long long m, long long start = 0) {
    if(m == 1) {
        return (start + n - 1) % n;
    }
    long long ans = 0;
    for(long long i = 2; i <= n;) {
        if(ans + m < i) {
            long long step;  ///判断步数
            if((i - ans - 1) % (m - 1) == 0) step = (i - ans - 1) / (m - 1) - 1;  ///如果能整除
            else step = (i - ans - 1) / (m - 1);  ///如果不能整除
            if(i + step > n) return ((ans + (n - i + 1) * m) + start) % n; ///若果迭代次数够了
            i += step;
            ans += step * m;
        }
        else {
            ans = (ans + m) % i; ///正常的迭代
            i++;
        }
    }
    return (ans + start) % n;
}
int main() {
    init();
    long long n, k;
    while(scanf("%lld%lld", &n,&k) != EOF) {
        long long ans = Josephus(n, k);
        printf("%lld\n", ans + 1);
    }
    return 0;
}








  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值