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;
}

 

posted @ 2017-08-15 22:16  chunlvxiong  阅读(112)  评论(0)    收藏  举报