码迷,mamicode.com
首页 > 其他好文 > 详细

51nod 1053 最大M子段和 V2

时间:2019-05-23 15:40:31      阅读:84      评论:0      收藏:0      [点我收藏+]

标签:code   思想   正数   相交   style   bind   clipboard   amp   压缩   

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\n",ans);
    }
    return 0;
}

 

51nod 1053 最大M子段和 V2

标签:code   思想   正数   相交   style   bind   clipboard   amp   压缩   

原文地址:https://www.cnblogs.com/8023spz/p/10912135.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!