【斜率优化】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; }