HDU 3045 Picnic Cows 斜率DP

Picnic Cows HDU - 3045
给一个序列 {a[i],i[1,n]}\{a[i],i\in[1,n]\} ,对这些数进行分组,每组至少 kk 个,每组内每个数都会减少到组中最小的那个数,求整体最小能减少多少?

首先肯定要排序,从小到大排序。

s[i]s[i] 为前缀和,dp[i]dp[i] 为前 ii 个数减少的最小值,状态转移为:

dp[i]=min{dp[j]+s[i]s[j]a[j+1]×(ij)} dp[i]=\min\{dp[j]+s[i]-s[j]-a[j+1]\times(i-j)\}

开始推斜率DP:令 k<jk<jjjkk 优,则:

dp[j]+s[i]s[j](ij)a[j+1]<dp[k]+s[i]s[k](ik)a[k+1]dp[j]s[j]+ja[j+1](dp[k]s[k]+ka[k+1])<ia[j+1]ia[k+1](dp[j]s[j]+ja[j+1])(dp[k]s[k]+ka[k+1])a[j+1]a[k+1]<i \begin{aligned} dp[j]+s[i]-s[j]-(i-j)a[j+1]&<dp[k]+s[i]-s[k]-(i-k)a[k+1]\\ dp[j]-s[j]+ja[j+1]-(dp[k]-s[k]+ka[k+1])&<ia[j+1]-ia[k+1]\\ \frac{(dp[j]-s[j]+ja[j+1])-(dp[k]-s[k]+ka[k+1])}{a[j+1]-a[k+1]}&<i \end{aligned}

yj=dp[j]s[j]+ja[j+1],xj=a[j+1]y_j=dp[j]-s[j]+ja[j+1],x_j=a[j+1],转移的时候再注意 i,ji,j 的范围。
代码如下:

#include<iostream>
#include<cstring>
#include<algorithm>
//#define WINE
#define MAXN 400010
using namespace std;
typedef long long ll;
int T,n,h,t,q[MAXN];
ll a[MAXN],s[MAXN],dp[MAXN];
ll up(int j,int k){
    return dp[j]-s[j]+j*a[j+1]-(dp[k]-s[k]+k*a[k+1]);
}
ll down(int j,int k){
    return a[j+1]-a[k+1];
}
ll getDP(int i,int k){
    return dp[k]+s[i]-s[k]-a[k+1]*(i-k);
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    while(scanf("%d%d",&n,&T)!=EOF){
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
        memset(dp,0,sizeof(dp));
        h=t=0;q[t++]=0;dp[T]=s[T]-s[1]*T;
        for(int i=T+1;i<=n;i++){
            while(h+1<t&&up(q[h+1],q[h])<i*down(q[h+1],q[h]))h++;
            dp[i]=getDP(i,q[h]);
            int j=i-T+1;
            if(j<T)continue;
            while(h+1<t&&up(j,q[t-1])*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2])*down(j,q[t-1]))
                t--;
            q[t++]=j;
        }
        printf("%lld\n",dp[n]);
    }
    return 0;
}

在这里插入图片描述

posted @ 2020-03-13 07:45  winechord  阅读(88)  评论(0编辑  收藏  举报