题目描述:
给出N个数的权值,以及一个参数K.
每次取出一段数的花费为 sum[l-r]^2+k
求取出这些数的最小花费
题目分析:
O(N2)DP
dp[i]=min(dp[j]+(sum[i]−sum[j])2)
O(N)斜率优化
dp[j]+sum[i]^2-2*sum[i]*sum[j]+sum[j]^2 < dp[k]+sum[i]^2-2*sum[i]*sum[k]+sum[k]^2
dp[j]-dp[k]+sum[j]^2-sum[k]^2 < 2*sum[i]*sum[j]-2*sum[i]*sum[k]
(dp[j]+sum[j]^2-dp[k]-sum[k]^2)/(2*(sum[j]-sum[k]))<sum[i]
题目链接:
AC 代码:
O(N2)DP
#include <cstdio>
#include <iostream>
#define ll long long
const int maxm=1e6+100;
ll dp[maxm],sum[maxm];
int main()
{
int n,k;
while((scanf("%d%d",&n,&k))!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%lld",&sum[i]);
sum[i]+=sum[i-1];
dp[i]=maxm;
}
dp[1]=sum[1]*sum[1]+k;
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++)
dp[i]=std::min(dp[i],dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j])+k);
printf("%lld\n",dp[n]);
}
return 0;
}
O(N)
#include <cstdio>
#include <iostream>
#define ll long long
const int maxm=510000;
ll dp[maxm],sum[maxm];
int dl[maxm];
double slop(int k,int j)
{
return (double)(dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k])/(double)(2*(sum[j]-sum[k]));
}
int main()
{
int n,k;
while((scanf("%d%d",&n,&k))!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%lld",&sum[i]);
sum[i]+=sum[i-1];
if(sum[i]==sum[i-1]) i--,n--;
}
int l=1,r=1;
dp[0]=0;
for(int i=1;i<=n;i++)
{
while(l<r&&slop(dl[l],dl[l+1])<=sum[i]) l++;
dp[i]=dp[dl[l]]+(sum[i]-sum[dl[l]])*(sum[i]-sum[dl[l]])+1ll*k;
while(l<r&&slop(dl[r-1],dl[r])>slop(dl[r],i)) r--;
dl[++r]=i;
}
printf("%lld\n",dp[n]);
}
return 0;
}