codeforces 940E(单调队列+dp)

题意:给你n个数字,要求将连续的数字划分成x组,每组的和为Sumi,每组长度即数字个数为Li,但每组前 Li/c(向下取整)小的数不计入总和,求最小的和 ∑sumi


明显的dp,dp[i]表示前i个数取最优分组的结果
最容易想到的是n2的,也必然是TLE的解法(因为n是1e5)
dp[i] = min(dp[j] + sum[i]-sum[j] - sum(Minsum((i-j)/c)))
枚举1到j,将1到j中前(i-j)/c小的去除即可
但是这样不仅超时,也没有一个好方法记录不同(i-j)间前几小的
想了很久我也不知道怎么写了,菜呀
好了我的思路就夭折了= =。
于是看了题解发现可以从性质入手
比如现在有10个数,c = 3,10 9 8 7 6 5 4 3 2 1
那么从下标(3,8)中需要剔除2个数,必然是3和4,也就是2C的情况,但是如果变成C+C的情况,(3,5),(6,8)那么剔除的就是3和6,所以C的情况一定是最优的
也就是此时只主需要从i-c转移过来,而不需考虑(i-c-1)之后的
再只需与dp[i-1]+a[i]比较就行了
不需要dp[i-2] + a[i] + a[i-1] 是因为dp[i-2]+a[i-1]就是dp[i-1](我的理解)


#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <cmath>
#define LL long long
#define INF 1000000000
#define mod 10056
const int maxn = 1e5 + 5;
using namespace std;
LL a[maxn];
LL dp[maxn];
LL sum[maxn];
LL q[maxn];
int main(){
    int n,c;
    scanf("%d%d",&n,&c);
    for(int i=1; i<=n; i++){
        scanf("%lld",&a[i]);
        sum[i] = sum[i-1] + a[i];
    }
    int head = 1, tail = 0;
    for(int i=1; i<=n; i++){
        while(head <= tail && q[head] < i-c+1) head++;//不在范围内
        while(head <= tail && a[q[tail]] > a[i]) tail--;//单调递增
        q[++tail] = i;
        dp[i] = dp[i-1] + a[i];
        if(i >= c) dp[i] = min(dp[i], dp[i-c] + (sum[i] - sum[i-c] - a[q[head]]));
    }
    printf("%lld\n",dp[n]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值