zoukankan      html  css  js  c++  java
  • 51nod 1053 最大M子段和 V2

    N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。
    例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。
     

    输入

    第1行:2个数N和M,中间用空格分隔。N为整数的个数,M为划分为多少段。(2 <= N , M <= 50000)
    第2 - N+1行:N个整数(-10^9 <= a[i] <= 10^9)

    输出

    输出这个最大和

    输入样例

    7 2
    -2
    11
    -4
    13
    -5
    6
    -2

    输出样例

    26


    v2果然是难了,还用dp就超时了,学无止境,本道题采用特别巧妙的贪心思想的方法,把相邻的正数或者负数,都加到一起,形成新的序列,也就是正数和负数交叉的序列,假如原序列是1 2 -3 -4 3,压缩后:
    3 -7 3,存到一个新的数组里,我们用ans记录所有正数的和,显然新数组里每一项都是一段,我们还要记录所有正数段得到个数,我们要保持正数段的个数在m之内,所以我们把所有的数段的绝对值,以及位置,存到一个set里,
    也可以用链表,这样做的目的是用负数填补空缺,从而把两个正数段合并为一段,我们需要知道每一段左右分别是谁,方便合并,不是单纯的下表加1和减1,因为合并多了,中间会有很多无效的位置,当然了用链表就不需要考虑这些问题,直接删除节点即可。
    那么怎么来合并呢,set里存绝对值和位置组成的pair,这样排序就按照绝对值排序了,每次选择绝对值最小的,假如这个数是正的,那么就用ans减去,然后跟两边的合并,因为set里所有的负数绝对值都比他大,所以ans肯定不包括它了,如果是负数,也是ans减去绝对值,负数跟两边的整数合并了,实际上让ans减少了。
    代码:
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <set>
    using namespace std;
    typedef long long ll;
    int n,m;
    ll d,last;
    ll s[50005];
    int l[50005],r[50005];
    int sc;
    void modify(int cur) {///修改左右相邻结点的下标
        int ll = l[cur],rr = r[cur];
        if(ll) {
            r[ll] = rr;
        }
        if(rr) {
            l[rr] = ll;
        }
    }
    int main() {
        while(~scanf("%d%d",&n,&m)) {
            ll sum = 0,ans = 0,c = 0;
            set<pair<ll,int> > ss;
            for(int i = 0;i < n;i ++) {
                scanf("%lld",&d);
                if(d * last < 0) {
                    s[++ sc] = sum;
                    if(sc == 1 && sum < 0) sc --;
                    sum = d;
                }
                else sum += d;
                last = d;
            }
            if(sum > 0) s[++ sc] = sum;
            c = (sc + 1) / 2;
            for(int i = 1;i <= sc;i ++) {
                ss.insert(make_pair(abs(s[i]),i));
                ans += (s[i] > 0 ? s[i] : 0);
                l[i] = i - 1;
                r[i] = i + 1;
            }
            r[sc] = 0;
            while(c > m) {
                int cur = ss.begin() -> second;
                ss.erase(ss.begin());
                if(s[cur] < 0 && (!l[cur] || !r[cur])) continue;///如果是负数,而且是处在首尾的位置那么就没必要合并了。
                ans -= abs(s[cur]);
                s[cur] += s[l[cur]] + s[r[cur]];
                if(l[cur]) {
                    ss.erase(make_pair(abs(s[l[cur]]),l[cur]));
                    modify(l[cur]);
                }
                if(r[cur]) {
                    ss.erase(make_pair(abs(s[r[cur]]),r[cur]));
                    modify(r[cur]);
                }
                if(s[cur]) ss.insert(make_pair(abs(s[cur]),cur));
                c --;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    arcgis建立拓扑分析(检验矢量图)
    矢量图面层和线层相交得到相交后的线层文件(gis相交)
    关于处理注册表权限无法修改的问题(无法打开主键或注册表项unknown)
    my.cnf配置文件
    keepalived联动mysql
    安装客户端连接软件(zabbix_agentd-filebeat-flume)
    zabbix短信配置说明
    zabbix监控工具添加网络设备Ping
    新买了硬盘,让Linux系统识别硬盘nfs---->改成xfs
    vnc软件使用
  • 原文地址:https://www.cnblogs.com/8023spz/p/10912135.html
Copyright © 2011-2022 走看看