hdu3045 Picnic Cows 斜率优化dp

题目大意:
给出一个有N (1<=N<=400000)个正数的序列,要求把序列分成若干组(可以打乱顺序),每组的元素个数不能小于T (1 < T <= N)。每一组的代价是每个元素与最小元素的差之和,总代价是每个组的代价之和,求总代价的最小值。

承接上一篇再刷一道题,唯一不同就是有一个区间限定。
不过多阐述了,直接贴代码。要看斜率优化分析,请转至
斜率优化

/*
首先 sort 从小到大排序。
然后 dp[i] 前 i 个数分隔得到的最小值。
则  dp[i]= min{dp[j]+(sum[i]-sum[j])-a[j+1](i-j)}    k<=j<i-t+1.
之后就是斜率优化了。
*/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
const int MAXN = 400010;

int que[MAXN];
LL sum[MAXN], a[MAXN], dp[MAXN];

LL getDP(int i, int j){
    return dp[j] + (sum[i] - sum[j]) - a[j + 1] * (i - j);
}

LL getUP(int j, int k){
    return dp[j] - sum[j] + j * a[j + 1] - dp[k] + sum[k] - k * a[k + 1];
}

LL getDOWN(int j, int k){
    return a[j + 1] - a[k + 1];
}

/*
定义状态dp[i]表示排序后前i个数分组后的最小代价。
dp[i] = dp[j] + (sum[i] –sum[j]) –a[j+1] * (i-j)
dp[i] = dp[k] + (sum[i] –sum[k]) –a[k+1] * (i-k)
斜率优化维护一个凸包,使队首的第一个线段的斜率最优
需要 dp[j] + (sum[i] –sum[j]) – a[j+1] * (i-j) > dp[k] + (sum[i] –sum[k]) - a[k+1] * (i-k)
所以 dp[j] + sum[i] –sum[j] –a[j+1] * i + a[j+1] * j > dp[k] + sum[i] - sum[k] - a[k+1] * i + a[k+1] * k)
所以 dp[j] –sum[j] –a[j+1] * i + a[j+1] * j > dp[k] - sum[k] - a[k+1] * i + a[k+1] * k)
所以 dp[j] - dp[k] - sum[j] + sum[k] + a[j+1] * j - a[k+1] * k > i * (a[j+1] - a[k+1])
     UP > i * DOWN
*/

int main(){
    int n, t;
    int head, tail;
    while(scanf("%d%d", &n, &t) == 2){
        dp[0] = sum[0] = a[0] = 0;
        for(int i=1;i<=n;i++){
            scanf("%I64d", &a[i]);
        }
        sort(a+1, a+n+1);
        for(int i=1; i<=n; i++)
            sum[i] = sum[i-1] + a[i];
        head = tail = 0;
        que[tail++] = 0;
        for(int i=1; i<=n; i++){
            while(head + 1 < tail 
            && getUP(que[head+1], que[head]) 
               <= i * getDOWN(que[head+1], que[head])) head++;
            dp[i] = getDP(i, que[head]);
            int j = i - t + 1;//不能超过
            if(j < t) continue;
            while(head + 1 < tail 
            && getUP(j, que[tail-1]) * getDOWN(que[tail-1], que[tail-2]) 
               <= getUP(que[tail-1], que[tail-2]) * getDOWN(j, que[tail-1])) tail--;
               /*原型是 
               getUP(j, que[tail-1]) / getDOWN(j, que[tail-1])
               <= getUP(que[tail-1], que[tail-2]) / getDOWN(que[tail-1], que[tail-2])
               但是为了防止炸精度,用交叉相乘 
               */
            que[tail++] = j;
        }
        printf("%I64d\n", dp[n]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值