zoukankan      html  css  js  c++  java
  • 倍增算法

    题目:

    有一个喜欢动画的少年,他希望每周都能收看尽可能多的动画。每一部动画都在每周固定的时间段播出。不过,由于他不喜欢录下电视节目留到以后观看,因此只能在播出时观看。此外,他每周看的动画都是固定的,并且他不可能同时观看播出时间有重叠的两部动画。在他的国家,一周共被划分成了M个单位的时间。现在假设一共有N部动画分别在每周的si时刻开始播放并在ti时刻播放结束,问每周他最多能看多少部动画。

    Input:

    3

    10

    0 3

    3 7

    7 0

    Output:

    3

    Input:

    3

    10

    0 5

    2 7

    6 9

    Output:

    2


    解析:

    本题可以看成在圆周上有N个区间,要从中选出尽可能多互不相交的区间的问题。和这个问题类似的有区间调度问题(请参照2.2.2)。本题仅仅是把那个问题的时间全集首尾相连形成一个圆周而已。区间调度问题可以根据结束时间排序之后使用贪心法求解。本题是否也可以用同样的方法求解呢?首先我们先确定一个要选择的区间。这样,和这个区间不相交的区间就不存在圆周,从而变成了简单的区间调度问题。因此,我们可以得到这样一个0(N^2)的算法。

    可以看成一个圆周上有N个区间,找出N个区间中尽可能多的互不相交的区间。选定一个区间作为起始区间后,贪心思想是:每次尽量选取与当前区间不重合且结束时间最早的区间。那么贪心策略还可以这样考虑,对于某区间i,选择区间j满足(i的结束时间t[i]<j的开始时间s[j]),从这些j中选择一个结束时间最早区间(记为k)作为区间i的下一个区间,记为next[i]=k;

    现在我们对于某一个区间i,已经算出了的下一个应该使用的区间是next[i]。利用这个值,我们可以算出下一个区间的再下一个区间是next2[i]=next[next[i]]。然后可以利用2个之后的区间next2算出4个之后的区间next4=next2[next2[i]]。不断重复类似的计算,就可以在O(N*k)时间内预处理出2^k个之后的区间next(k)[i]。如果得到了对于k=floor(log N)的预处理结果,那么就可以把贪心法中一个区间一个区间地遍历,改为使用二分搜索的方式。对于一个初始选取的区间计算的复杂度是O(logN)。因此整个算法的复杂度就是O(NlogN)。

    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #include<string>
    #include<cmath>
    using namespace std;
    #define N_MAX 100000+20
    #define NLOG 30
    pair<int, int>ps[4*N_MAX];
    int s[N_MAX*4+3],t[N_MAX*4+3];
    int Next[NLOG][N_MAX];
    int n, m;
    
    bool cmp(const pair<int,int>p1,const pair<int,int>p2) {
        if (p1.first != p2.first)return p1.first < p2.first;
        else return p1.second > p2.second;//若某一个区间头部和另一个区间的尾部时间点重合,尾部在前!!
    }
    
    int main() {
        while (scanf("%d%d",&n,&m)!=EOF) {
            for (int i = 0; i < n; i++) {
                scanf("%d%d",&s[i],&t[i]);
            }
            for (int i = 0; i < n;i++) {
                if (s[i] > t[i])t[i] += m;
                s[i + n] = s[i] + m;
                t[i + n] = t[i] + m;
            }
            for (int i = 0; i < 2*n;i++) {
                ps[i] = make_pair(s[i], i);//区间头部
                ps[i+2*n] = make_pair(t[i], i+2*n);//区间尾部
            }
            sort(ps, ps + 4 * n,cmp);
            int last = -1;
            memset(Next,-1,sizeof(Next));
            for (int i = 4*n-1; i >=0;i--) {//!!!!!
                int id = ps[i].second;
                if (id < 2 * n) {
                    if (last == -1 || t[last] > t[id]) last = id;
                }
                else {
                    id -= 2 * n;
                    Next[0][id] = last;
                }
            }
            for (int k = 0; k +1< NLOG;k++) {
                for (int i = 0; i < 2*n;i++) {
                    if (Next[k][i] == -1)Next[k + 1][i] = -1;
                    else
                        Next[k + 1][i] = Next[k][Next[k][i]];
                }
            }
            int max_cnt = 0;
            for (int i = 0; i < n;i++) {//选定一个初始城市
                int j = i,cnt=0;//j代表当前所在城市
                for (int k = NLOG - 1; k >= 0;k--) {
                    if (Next[k][j] >= 0 && t[Next[k][j]] <= s[i] + m) {
                        j = Next[k][j];
                        cnt |= 1 << k;
                    }
                }
                max_cnt = max(max_cnt, cnt+1);//不要忘了一开始选为起点的区间
            }
            printf("%d
    ",max_cnt);
        }
        return 0;
    }
    

      

    加油啦!加油鸭,冲鸭!!!
  • 相关阅读:
    个人永久性免费-Excel催化剂功能第86波-人工智能之图像OCR文本识别全覆盖
    git删除远程仓库的文件或目录
    使用document.execCommand复制内容至剪贴板
    使用clipboard.js实现复制内容至剪贴板
    正则匹配目标字符串不等于某些特定的字符串
    substr与substring的用法
    使用hexo搭建个人博客
    git修改最后一次commit的内容
    gulp压缩文件最简示例
    gulp最简示例
  • 原文地址:https://www.cnblogs.com/clarencezzh/p/10345247.html
Copyright © 2011-2022 走看看