HDU3507-Print Article
chunlvxiong的博客
题目描述:
用一台打字机打印单词,每个单词都有一个代价值ci,连续打印k个单词的代价为(Σci)^2+m,问打印完所有n个单词的最小代价是多少?(1≤n≤500000)
思考&分析:
很快能想到O(N^2)的DP,DP方程:dp[i]=min(dp[j]+(sum[i]-sum[j])^2+m|0<=j<i),但n=500000-->TLE。
这时候你需要考虑斜率优化:
假设dp[i]从dp[j]转移比从dp[k]转移优(j>k),那么
dp[j]+(sum[i]-sum[j])^2+m<dp[k]+(sum[i]-sum[k])^2+m
-->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]+sum[j]^2)-(dp[k]+sum[k]^2)<2*sum[i]*(sum[j]-sum[k])
设yi=dp[i]+sum[i]^2,xi=sum[i]。
那么当(yj-yk)/(xj-xk)<2*sum[i],dp[i]从dp[j]转移比从dp[k]住转移优(j>k)
如果g(i,j)<g(j,k):(i>j>k)
1、g(i,j)<2*a*sum[i]-->i比j优
2、g(i,j)>2*a*sum[i],g(j,k)>g(i,j)>2*a*sum[i]-->j比k劣
所以j永远不会成为决策点。
然后利用单调队列进行维护,你可以把整个问题时间复杂度降为O(N)。需要注意一件事:即sum[j]可能等于sum[k](j>k),此种情况,计算斜率结果直接返回oo即可。
贴代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=500005; const ll oo=1e18; int n,m,front,rear,Q[maxn]; ll c[maxn],sum[maxn]; ll dp[maxn]; ll y(int a,int b){ return (dp[a]+sum[a]*sum[a])-(dp[b]+sum[b]*sum[b]); } ll x(int a,int b){ return sum[a]-sum[b]; } double g(int a,int b){ ll Y=y(a,b),X=x(a,b); if (X) return Y/X; else return oo; } int main(){ while (~scanf("%d%d",&n,&m)){ sum[0]=0; for (int i=1;i<=n;i++) scanf("%d",&c[i]),sum[i]=sum[i-1]+c[i]; dp[0]=front=rear=0; Q[rear++]=0; for (int i=1,j;i<=n;i++){ while (front<rear-1 && g(Q[front+1],Q[front])<2.0*sum[i]) front++; j=Q[front],dp[i]=dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j])+m; while (front<rear-1 && g(i,Q[rear-1])<g(Q[rear-1],Q[rear-2])) rear--; Q[rear++]=i; } printf("%lld\n",dp[n]); } return 0; }