您的位置:首页 > 其它

HDU 3045

2013-08-12 21:44 281 查看
斜率优化dp

第一次做斜率优化dp,惯例写详细点。。

题意:给你N个数,将它们分成若干组,要求每组至少包含T个数。将每一组的数全部变为该组最小的数。进行合适的分组,使得数字的减小量的总和最小。求这个最小值。

    首先很容易想到将这N个数字从小到大排序。递推式就很容易出来:dp[j] = min(dp[i] + sum[j] - sum[i] - num[i+1]*(j-i))  i<=j-T  其中,dp[i] 表示将前 i 个数分组的最小费用,sum[i]表示前 i 个数的和,num[i]表示第 i 个数。由于题目给出的 N 最大有 4*10^5 直接推,两个for循环超时妥妥的。这里用到了斜率优化,将复杂度从O(n^2) 降到O(n)

    假设 i<j<p ,那么对于 p 从i,j递推来的两个值分别为:dp[i] + sum[p] - sum[i] - num[i+1]*(p-i) 和 dp[j] + sum[p] - sum[j] - num[j+1]*(p-j) 。如果 i 不优于 j ,那么就一定满足:dp[j] + sum[p] - sum[j] - num[j+1]*(p-j) <= dp[i] + sum[p] - sum[i] - num[i+1]*(p-i)  整理后得:dp[j]-sum[j]+num[j+1]*j
- (dp[i]-sum[i]+num[i+1]*i) <= (num[j+1]-num[i+1])*p   令yj=dp[j]-sum[j]+num[j+1]*j ,xj=num[j+1] 那么上式就能整理为:(yj-yi)/(xj-xi) <= p 左边就是一个斜率表达式了。方便描述,这里记g[i,j] = (yj-yi)/(xj-xi)。如何利用这个斜率优化呢?

    优化1:假设 i<j<k<p , 如果g[j,k]<=g[i.j] 那么可以忽略点 j 。原因如下:当g[j,k]<=p ,对于 p 来说,j 不优于 k ;当 g[j,k]>p ,那么g[i,j]>=g[j,k]>p,对于 p 来说 i 优于 j 。去掉所有类似 j 的点后,斜率呈现严格递增。

    基于优化1有优化2:假设 i<j<p<q , 如果g[i,j]<=p ,那么对于点 p ,i 不优于 j ;对于点 q ,g[i,j]<=p<q ,i 劣于 j 。假设我们找到了 p 的最优解 j ,那么 j 以前的解一定不是 q 的最优解。

    有了这两个优化,复杂度就成功的降到了O(n)。结合代码细细体会。over

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;

#define Maxn 400010
int N,T,q[Maxn],head,tail;
long long num[Maxn],dp[Maxn],sum[Maxn];

long long getDp(int i,int j);
long long getY(int i,int j);
long long getX(int i,int j);
int main()
{
while(scanf("%d%d",&N,&T)!=EOF)
{
dp[0]=sum[0]=num[0]=head=tail=0;
q[tail++]=0;
for(int i=1;i<=N;i++)
{
scanf("%I64d",&num[i]);
sum[i]=sum[i-1]+num[i];
}
sort(num+1,num+1+N);
for(int i=T;i<=N;i++)
{
while(head+1<tail && getY(q[head],q[head+1])<=i*getX(q[head],q[head+1]))
head++;
dp[i]=getDp(q[head],i);
int j=i-T+1;
if(j<T)
continue;
while(head+1<tail && getY(q[tail-1],j)*getX(q[tail-2],q[tail-1])<=getY(q[tail-2],q[tail-1])*getX(q[tail-1],j))
tail--;
q[tail++]=j;
}
printf("%I64d\n",dp
);
}
return 0;
}

long long getDp(int i,int j)
{
return dp[i]+sum[j]-sum[i]-num[i+1]*(j-i);
}
long long getY(int i,int j)
{
return dp[j]-sum[j]+num[j+1]*j-(dp[i]-sum[i]+num[i+1]*i);
}
long long getX(int i,int j)
{
return num[j+1]-num[i+1];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息