• Haybale Stacking(差分数组 + 求中位数的一些方法 + nth_element)


    题意:

    给定N个初始值为0的数, 然后给定K个区间修改(区间[l,r] 每个元素加一), 求修改后序列的中位数。

    分析:

    K个离线的区间修改可以使用差分数组(http://www.cnblogs.com/Jadon97/p/8053946.html)实现。

    关于对一个无序的序列找出中位数

    方法一:

     第一时间想到的方法是快排然后之间取中位数, 排序复杂度为O(NlogN),取第k大的数复杂度O(1)。

    用时:124ms

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1000000 + 7;
    int N, K;
    int a[maxn], d[maxn];
    int main(){
        cin >> N >> K;
        d[1] = a[1];
        for(int i = 0; i < K; i++){
            int a, b;
            cin >> a >> b;
            d[a]++, d[b+1]--;
        }
        for(int i = 1; i <= N; i++){
            a[i] = a[i-1] + d[i];
        }
        sort(a+1,a+1+N);
        cout << a[N/2+1] << "
    ";
        return 0;
    }
    快排

    方法二:

    但其实还有有一个O(n)的算法,是利用快排的思想,详情可查看partiton算法http://blog.jobbole.com/105219/),这个算法可以延伸到求第k大的数

    • pos == k,则找到第 K 小的值,arr[pos];
    • pos > k,则第 K 小的值在左边部分的数组。
    • pos < k,则第 K 大的值在右边部分的数组。

    而在最好情况下,每次将数组均分为长度相同的两半,运行时间 T(N) = N + T(N/2),时间复杂度是 O(N), 但是这个算法最坏情况是O(N²),暂时没想到很好的优化方法。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1000000 + 7;
    int N, K;
    int a[maxn], d[maxn];
    int partition(int begin, int end) //[begin,end]左闭右闭区域
    {
        int pivot = a[begin];//选取第一个数为枢轴
        while(begin < end)
        {
            while(begin < end && a[end] >= pivot) end--; //在后面选取一个小于枢轴的数
            a[begin] = a[end]; //将那个数放到前面
            while(begin < end && a[begin] <= pivot) begin++; //在前面选取一个大于枢轴的数
            a[end] = a[begin];//将那个数放到后面
        }
        a[begin] = pivot;//最后将枢轴放回
        return begin;//返回枢轴的下标
    }
    
    int find_kth_number(int k){
        int begin = 1, end = N;
        int target_num = 0;
        while (begin <= end){
            int pos = partition(begin, end);//查看枢轴的位置
            if(pos == k){//如果枢轴 == k, 那么枢轴就是第k小的数
                target_num = a[pos];
                break;
            }
            else if(pos > k){//否则从左边找
                end = pos - 1;
            }
            else{//否则从右边找
                begin = pos + 1;
            }
        }
        return target_num;
    }
    int main(){
        scanf("%d %d", &N, &K);
        d[1] = a[1];
        for(int i = 0; i < K; i++){
            int a, b;
            scanf("%d %d", &a, &b);
            d[a]++, d[b+1]--;
        }
        for(int i = 1; i <= N; i++){
            a[i] = a[i-1] + d[i];
        }
        printf("%d
    ", find_kth_number(N/2+1));
        return 0;
    }
    parition算法

    但是有一个STL库函数eth_element(http://zh.cppreference.com/w/cpp/algorithm/nth_element), 思想应该是差不多的, 但因为优化原因运行时间可以过这题, 所以可以使用这个库函数快速求出第k大的数。

    用时:74ms

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1000000 + 7;
    int N, K;
    int a[maxn], d[maxn];
    int main(){
        scanf("%d %d", &N, &K);
        d[1] = a[1];
        for(int i = 0; i < K; i++){
            int a, b;
            scanf("%d %d", &a, &b);
            d[a]++, d[b+1]--;
        }
        for(int i = 1; i <= N; i++){
            a[i] = a[i-1] + d[i];
        }
        nth_element(a+1,a+N/2+1,a+1+N);
        printf("%d
    ", a[N/2+1]);
        return 0;
    }
    nth_element

    方法三:

    对于这题特殊的区间修改, 因为N多达1e6, 而K只有25000, 所以最大的数也只有K, 所以我们求出区间修改的值的时候,可以把每个数的出现次数记录下来, 然后循环K次,把出现次数累计起来。加上某个数累计次数大于等于N/2时, 那个数就是中位数,复杂度是(N+K),应该是最快的方法了。

    用时:49ms

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int maxn = 1234567;
     4 int N, K;
     5 int a[maxn], d[maxn];
     6 int cnt_num[25000 + 7];
     7 int main(){
     8     scanf("%d %d", &N, &K);
     9     d[1] = a[1];
    10     for(int i = 0; i < K; i++){
    11         int a, b;
    12         scanf("%d %d", &a, &b);
    13         d[a]++, d[b+1]--;
    14     }
    15     for(int i = 1; i <= N; i++){
    16         a[i] = a[i-1] + d[i];
    17         cnt_num[a[i]]++;//统计每个数字出现了多少次
    18     }
    19     int sum = 0, mid_num;
    20     for(mid_num = 0; mid_num <= K; mid_num++){
    21         sum += cnt_num[mid_num];
    22         if(sum > N/2) break;
    23     }
    24     printf("%d
    ", mid_num);
    25     return 0;
    26 }
    统计数字
  • 相关阅读:
    实验一 开发环境的熟悉 20165311
    2018-2019-2 20165307《网络对抗技术》Exp9 Web安全基础
    2018-2019-2 网络对抗技术 20165307 Exp 8 Web基础
    2018-2019-2 网络对抗技术 20165307 Exp7 网络欺诈防范
    2018-2019-2 20165307网络对抗技术 Exp6:信息收集与漏洞扫描
    2018-2019-2 20165307《网络对抗技术》Exp5 MSF基础应用
    2018-2019-2 网络对抗技术 20165307 Exp4 恶意代码分析
    2018-2019-2 网络对抗技术 20165307 Exp3 免杀原理与实践
    2018-2019-2 20165307《网络对抗技术》Exp2 后门原理与实践
    20165307《网络对抗技术》Exp1 PC平台逆向破解
  • 原文地址:https://www.cnblogs.com/Jadon97/p/8317267.html
Copyright © 2020-2023  润新知