洛谷P2300 合并神犇

传送门啦

分析:

刚开始读完题后感觉很懵,怎么算都不是3,结果发现题目理解错了。题目要求的是求一个不降的序列,不是递减的(发现自己好傻)

看明白题就好做了吧。经典的区间dp题,合并果子大家应该都做过,那个题求一个代价,这个题换成合并次数了,也差不多。

本题要使一段序列合并成不下降序列,因为只能合并相邻的两个数,所以合并后的一个数必定是由原版序列中的一段进行数次合并得到的。考虑简单的贪心思路,对于第一个数,每次不停加入一个数直到它们的和大于第一个数停止,继续此操作,直到结束,但是这样显然是错误的,因为前面满足了条件不一定后面会最优(很简单思考懒的举例了)。

由贪心思路引申到dp,因为是一段合并,考虑到前缀和sum[i],我们令f[i]表示到了第i个数为止所合并的次数,用一个辅助数组maxp[i]表示到了i为止合并后最大的一个数,于是得到状态转移方程:if(sum[i]-sum[j] >= maxp[j])f[i] = f[j] + j - i - 1(其中i > j)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200005;

inline int read(){
    char ch = getchar(); int f = 1 , x = 0;
    while(ch > '9' || ch < '0'){if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,p[maxn];
long long f[maxn],sum[maxn],maxp[maxn];

int main(){
    n = read();
    for(int i=1;i<=n;i++) {
        p[i] = read();
        sum[i] = sum[i-1] + p[i];
    }
    int i,j;
    for(i=1;i<=n;i++){
        for(j=i-1;j>=0;j--)
            if(sum[i] - sum[j] >= maxp[j])
                break;
        f[i] = f[j] + i - j - 1;
        maxp[i] = sum[i] - sum[j];
    }
    printf("%lld",f[n]);
    return 0;
}
顺风不浪,逆风不怂。
原文地址:https://www.cnblogs.com/Stephen-F/p/9864885.html