【斜率优化】HDU 3045 Picnic Cows

通道

题意:有n个数,分段,每段长度至少为T,这段权值为全修改为最小数花费,求总花费最小

思路:

 

先对n个数进行排序,dp[i]表示前i个数得到的最少值,则:从j+1~i作为一组 

dp[i]=dp[j]+sum[i]-sum[j]-(i-j)*s[j+1];//sum[i]表示前i个数的和
=>dp[i]=dp[j]+sum[i]-sum[j]+j*s[j+1]-i*s[j+1];
由于有i*s[j+1]这一项,所以无法直接在扫描数组的过程中用单调队列维护:
dp[j]-sum[j]+j*s[j+1]-i*s[j+1]的最小值。
考虑用斜率dp!
假定k<=j<i-t以j+1~i作为一组比以k+1~i作为一组更优
则:
dp[j]+sum[i]-sum[j]-(i-j)*s[j+1] <= dp[k]+sum[i]-sum[k]-(i-k)*s[k+1]
=>dp[j]+sum[i]-sum[j]+j*s[j+1]-i*s[j+1] <= dp[k]+sum[i]-sum[k]+k*s[k+1]-i*s[k+1]
=>(dp[j]-sum[j]+j*s[j+1] - (dp[k]-sum[k]+k*s[k+1]))/(s[j+1]-s[k+1])<=i;//保证s[j]>=s[k]
令:
y1 = dp[j]-sum[j]+j*s[j+1]
y2 = dp[k]-sum[k]+k*s[k+1]
x1 = s[j+1]
x2 = s[k+1]
所以变成了:
(y1 - y2)/(x1 - x2) <= i;

 

代码:

#include <cstdio>
#include <algorithm>

typedef long long LL;
using namespace std;

const int MAX = 400000 + 10;
int n,t,q[MAX];
LL s[MAX],sum[MAX],dp[MAX];

LL GetY(int j,int k){
    return dp[j]-sum[j]+j*s[j+1]-(dp[k]-sum[k]+k*s[k+1]);
}
LL GetX(int j,int k){
    return s[j+1]-s[k+1];
}
LL DP(){
    int head=0,tail=1;
    q[0]=0;
    for(int i=1;i<=n;++i)sum[i]=sum[i-1]+s[i];
    for(int i=0;i<=n;++i){//i从2*t开始 
        while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*i)++head;
        dp[i]=dp[q[head]]+sum[i]-sum[q[head]]+q[head]*s[q[head]+1]-i*s[q[head]+1];
        if(i-t+1<t)continue; 
        while(head+1<tail && GetY(i-t+1,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(i-t+1,q[tail-1]))--tail;
        q[tail++]=i-t+1;
    }
    return dp[n];
}

int main(){
    while(~scanf("%d%d",&n,&t)){
        for(int i=1;i<=n;++i)scanf("%I64d",s+i);
        sort(s+1,s+1+n);
        printf("%I64d\n",DP());
    }
    return 0;
}
View Code

 

posted @ 2015-08-02 14:39  mithrilhan  阅读(149)  评论(0编辑  收藏  举报