• 6.1 好题分享小结


    我好久没写博客了啊懒惰病上线,这个欢乐的节日听了HA最强女选手讲课,于是决定翘掉地理课写篇博客总结一下;

    内容大致是中位数,先看一下圆神讲课思路(这样我可以少阐明很多概念

     第一个就是引例啦:货仓选址

    我们设在仓库左边的所有点,到仓库的距离之和为p,右边的距离之和则为q,那么我们就必须让p+q的值尽量小。p<q向右移距离减少q-p,p>q,同理,所以当p=q时就是就是全局的最优解;

    排序之后找中位数;

    #include <bits/stdc++.h>
    using namespace std;
    
    int n,a[100010];
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i++) 
            cin >> a[i];
        sort(a + 1, a + n + 1);
        int id = a[n / 2 + 1];
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            ans = ans +  abs(a[i] - id);
        }
        cout << ans <<endl;
    }
    View Code

    第二题:七夕祭

    题目大意:n行m列,t个感兴趣的摊位,交换相邻的两个摊位,使得每行每列中cl感兴趣的摊位数量相同,特别注意的是,每一行的第一个位置和最后一个位置也是相邻,判断是否可行并且输出需要移动的步数;

    对于判断是否可行,对行来说,如果t无法整除n,那么行不可能达到目标,如果可以整除,我们的目标就是如何使每行中有t/n个感兴趣的摊位;列也是如此,不过两者一样,我们就在这里分析一种情况吧;

    我们在这里回想起一道经典的贪心题目“均分纸牌”:M个人排成一列,每个人手里若干张牌C【i】,交换相邻两个使得每个人手里的牌的数量相同;

    如果可行(总数T整除M),每个人需要得到T/,M张牌,那么需要的纸牌数为C【i】-T/M;每次i从i+1出拿牌,同时更新i+1,那么我们的目标就是每个A【i】=C【i】-T/M=0;

    另S【i】为A【i】的前缀和,

    #include<bits/stdc++.h>
    using namespace std;
    int main() {
        int n,q=0,a[100010],x[100010];
        cin>>n;
        for(int i=1;i<=n;i++) {
            cin>>a[i]; 
            q+=a[i];
        }
        q/=n;
        for(int i=1;i<=n;i++) {
            a[i]-=q;
        }
        int ans=0;
        for(int i=1;i<=n;i++) {
            if(!a[i]) continue;
            a[i+1]+=a[i];
            ans++;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    回到这个题,如果第一个位置与最后一个位置相邻,那么这是一个环形的均分纸牌;

    我们可以枚举断点k,但我们不妨分析,A[i]仍为减去T/M后的数组,当枚举到断点k,前缀和变为S【i】减去S【k】,那么答案为∑|s【i】-s【k】|,那么这个答案何时最小呢,就是上道题的货仓选址,也就是中位数的时候,所以我们不需要枚举k,只需要排序后找中位数作为s【k】;这时就是最优解;

    时间复杂度O(NlogN+MlogM);

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100010
    #define ll long long 
    template<typename T>inline void read(T &x) {
        x=0; T f=1,ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
        x*=f;
    }
    
    ll n,m,t,x,y,h[N],l[N],f[N];
    
    inline ll calc(ll b[N],int n) {
        ll ans=0;
        for(int i=1;i<=n;i++) {
             b[i]-=b[0]/n;
             f[i]=f[i-1]+b[i];
        }
        sort(f+1,f+n+1);
        for(int i=1;i<=n;i++) ans+=abs(f[i]-f[n+1>>1]);
        return ans;
    }
    
    int main() {
        read(n); read(m); read(t);
        for(int i=1;i<=t;i++) {
            read(x); read(y);
            h[x]++; l[y]++;
        }
        for(int i=1;i<=n;i++)  h[0]+=h[i];
        for(int i=1;i<=m;i++)  l[0]+=l[i];
        if(h[0]%n==0&&l[0]%m==0) {
            printf("both ");
            printf("%lld
    ",calc(h,n)+calc(l,m));
        }
        else if(h[0]%n==0) {
            printf("row ");
            printf("%lld
    ",calc(h,n));
        }
        else if(l[0]%m==0) {
            printf("column ");
            printf("%lld
    ",calc(l,m));
        }
        else printf("impossible");
        return 0;
    }
    View Code

    最后一道题目啦:动态中位数

    读入序列,为奇数时,输出已读序列中位数;

    我这里只写一种在线的堆做法,当然链表也可以;

    我们可以建立两个堆,一个大根堆一个小根堆,对于一个M长度的序列,从小打到1~M/2的放在大根堆,M/2+1~M的我们放在小根堆,这样每次的小根堆的堆顶就是答案,任何时候如果一个堆中的序列数过多,就从堆顶取出堆顶插入另一个堆,每次插入X和中位数比较,如果X大,就插入大根堆,否则插入小根堆;

    #include <algorithm>
    #include <cctype>
    #include <cmath>
    #include <complex>
    #include <cstdio>
    #include <cstring>
    #include <deque>
    #include <functional>
    #include <list>
    #include <map>
    #include <iomanip>
    #include <iostream>
    #include <queue>
    #include <set>
    #include <stack>
    #include <string>
    #include <vector>
    #include <bitset>
    using namespace std;
    template<typename T>inline void read(T &x) {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x*=f;
    }
    int T,n,m,x;
    vector<int> g;
    priority_queue<int,vector<int>,greater<int> >q1;
    priority_queue<int,vector<int>,less<int> > q2;
    inline void add(int x) { 
        if(q1.empty()) {
            q1.push(x);
            return ;
        }
        if(x>q1.top()) q1.push(x);
        else q2.push(x);
        while(q1.size()>q2.size()) {
            q2.push(q1.top());
            q1.pop();
        }
        while(q2.size()>q1.size()) {
            q1.push(q2.top());
            q2.pop();
        }
    }
    int main() {
        read(T);
        while(T--) {
            while(q1.size()) q1.pop();
            while(q2.size()) q2.pop();
            g.clear();
            read(n);read(m);
            for(int i=0;i<m;i++) {
                read(x);
                add(x);
                if(i%2==0) g.push_back(q1.top());
            }
            printf("%d %d
    ",n,(m+1)/2);
            for(int i=0;i<g.size();i++) {
                if(i>0&&i%10==0) putchar('
    ');
                if(i%10) putchar(' ');
                printf("%d",g[i]);
             }
             putchar('
    ');
        }
        return 0;
    }
    View Code
  • 相关阅读:
    nginx源代码分析--从源代码看nginx框架总结
    [Android]自己定义带删除输入框
    A7139 无线通信驱动(STM32) 添加FIFO扩展模式,能够发送超大数据包
    cmake使用演示样例与整理总结
    Hibernate也须要呵护——Hibernate的泛型DAO
    hdoj-1242-Rescue【广搜+优先队列】
    五类常见算法小记 (递归与分治,动态规划,贪心,回溯,分支界限法)
    动态标绘演示系统1.4.3(for ArcGIS Flex)
    CodeForces
    OpenCV——颜色运算
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/10974981.html
Copyright © 2020-2023  润新知