HDU 3045 Picnic Cows 斜率DP
Picnic Cows HDU - 3045
给一个序列 ,对这些数进行分组,每组至少 个,每组内每个数都会减少到组中最小的那个数,求整体最小能减少多少?
首先肯定要排序,从小到大排序。
令 为前缀和, 为前 个数减少的最小值,状态转移为:
开始推斜率DP:令 , 比 优,则:
得 ,转移的时候再注意 的范围。
代码如下:
#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;
}