• NOJ 1190 约瑟夫问题 线段树OR树状数组


    题目链接


    约瑟夫问题
    时间限制(普通/Java) : 1000 MS/ 3000 MS          运行内存限制 : 65536 KByte
    总提交 : 607            测试通过 : 57 
    比赛描述
    约瑟夫问题是一个非常经典的问题,它的问题描述是:有 n 个人围成一圈,从第 1 个人开始,每次按顺时针方向向后选择第 m 个人,并将这个人出列。那么你能高效的算出出列的顺序吗?
    
    输入
    输入数据有多组,每组输入数据为一行,两个正整数 n和m (1<=n,m<=30000)
    
    输出
    每组输出只有一行,表示出列的顺序。每两个数字之间用一个空格分开。
    
    样例输入
    4 2
    5 3
    
    样例输出
    2 4 3 1
    3 1 5 2 4
    
    

    很不错的一个题目,常用方法时数组或者链表模拟,会TLE,如果求最后剩下一个人的编号可以用数学方法计算。

    思路

    将每次出列的人拿掉,从左往右形成一个新的序列,每次从上一个出列的人所处为的位置考虑,找出下一个出列的人在新序列中的位置。

    使用线段树或者树状树组可以很好的找到新序列中的第k个人的位置。

    树状数组

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    #include <vector>
    #include <algorithm>
    #include <queue>
    #include <map>
    #include <stack>
    #include <string>
    #include <math.h>
    #include <bitset>
    #include <ctype.h>
    #include <set>
    using namespace std;
    typedef pair<int,int> P;
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const double PI = acos(-1.0);
    const double eps = 1e-9;
    const int N = 3e4 + 5;
    const int mod = 1e9 + 7;
    int n, c[N];
    inline int lowbit(int x)
    {
        return x&(-x);
    }
    void add(int x, int val)
    {
        while(x <= n)
        {
            c[x] += val;
            x += lowbit(x);
        }
    }
    int sum(int x)
    {
        int ans = 0;
        while(x)
        {
            ans += c[x];
            x -= lowbit(x);
        }
        return ans;
    }
    int m;
    int main()
    {
        while(scanf("%d%d", &n, &m) != EOF)
        {
            memset(c, 0, sizeof(c));
            for(int i = 1; i <= n; i++)
                add(i,1);
            int num = n, i = 0;
            while(num > 1)
            {
                int j = (i+m-1)%num;
                int p = j+1;
                int l = 1, r = n, mid;
                while(l <= r)
                {
                    mid = (l+r) >> 1;
                    if(sum(mid) >= p) r = mid - 1;
                    else l = mid + 1;
                }
                printf("%d ",r+1);
                add(r+1, -1);
                i = j % (num-1);
                num--;
            }
            int l = 1, r = n;
            while(l <= r)
            {
                int mid = (l+r) >> 1;
                if(sum(mid) >= 1) r = mid - 1;
                else l = mid + 1;
            }
            printf("%d
    ", r+1);
        }
        return 0;
    }
    

    线段树

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    #include <vector>
    #include <algorithm>
    #include <queue>
    #include <map>
    #include <stack>
    #include <string>
    #include <math.h>
    #include <bitset>
    #include <ctype.h>
    #include <set>
    using namespace std;
    typedef pair<int,int> P;
    typedef long long LL;
    const int INF = 0x3f3f3f3f;
    const double PI = acos(-1.0);
    const double eps = 1e-9;
    const int N = 3e4 + 5;
    const int mod = 1e9 + 7;
    int sum[N<<2];
    int n,m;
    int ans[N];
    void build(int rt, int l, int r)
    {
        if(l == r)
        {
            sum[rt] = 1;
            return;
        }
        int m = (l+r) >> 1;
        build(rt << 1 , l, m);
        build(rt << 1|1, m+1, r);
        sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    }
    
    void update(int pos, int val, int rt, int l, int r)
    {
        if(l == r)
        {
            sum[rt] = 0;
            return ;
        }
        int m = (l+r) >> 1;
        if(pos <= m) update(pos, val, rt<<1, l, m);
        else update(pos, val, rt<<1|1, m+1, r);
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    }
    
    int query(int pos, int rt, int l, int r)
    {
        if(l == r)
            return l;
        int m = (l+r) >> 1;
        if(pos > sum[rt<<1]) return query(pos - sum[rt<<1], rt<<1|1, m+1, r);
        else
            return query(pos, rt<<1, l, m);
    }
    
    int main()
    {
        while(EOF != scanf("%d%d", &n,&m))
        {
            build(1,1,n);
            int len = n;
            int i = 0;
            int pos = 0;
            while(len > 1)
            {
                int p = (i + m-1)%len;
                ans[pos++] = query(p+1, 1, 1, n);
                update(ans[pos-1], 0, 1, 1, n);
                i = p%(len - 1);
                len--;
            }
            ans[pos++] = query(1,1,1,n);
            for(int i = 0; i < pos; i++)
            {
                if(i) printf(" ");
                printf("%d", ans[i]);
            }
            printf("
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    解决SecureCRT连接linux终端中文显示乱码
    处理二维数组两题
    struts2校验总结
    专题:分治法
    常用表格编写
    写Action的三种方法
    Struts2入门-十分简单的登陆
    汉诺塔动画
    记录一下字符串处理的问题
    记录一些指针的应用方法
  • 原文地址:https://www.cnblogs.com/Alruddy/p/7416300.html
Copyright © 2020-2023  润新知