bsoj 3733 【模拟试题】打印文章(hdu3507)

Description
给出N个单词,每个单词有个非负权值Ci,现要将它们分成连续的若干段,每段的代价为此段单词的权值和,还要加一个常数M,即(∑Ci)^2+M。现在想求出一种最优方案,使得总费用之和最小。
Input
包含多组测试数据,对于每组测试数据。第一行包含两个整数N和M(0 <= N <= 500000,0 <= M <= 1000),第二行为N个整数。
Output
输出仅一个整数,表示最小的价值。
Sample Input
5 5
5 9 5 7 5
Sample Output
230
刚学了斜率优化:
F[i]=min(F[j]+(S[i]-S[j])^2)+M;
设j< k
G[j]=F[j]+(S[i]-S[j])^2;
假设 G[j]>G[k]
F[j]+(S[i]-S[j])^2>F[k]+(S[i]-S[k])^2
(S[i]-S[j])^2=S[i]^2-2*S[i]*S[j]+S[j]^2
F[j]-F[k]+S[j]^2-S[k]^2>2*S[i]*(S[j]-S[k])
∵S[k]>S[j]
∴(F[j]-F[k]+S[j]^2-S[k]^2)/(S[j]-S[k])<2*S[i];
令Slope[j][k]=(F[j]-F[k]+S[j]^2-S[k]^2)/(S[j]-S[k]);
当Slope[j][k]<2*S[i]时,k比j优

初略算了一下。
好像还行吧.


#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
long long S[500005];
long long f[500005];
int q[500005];
double Slope(int j,int k){
    return (f[j]-f[k]+S[j]*S[j]-S[k]*S[k])*1.0/(S[j]-S[k]);
}
int main(){
    int n;
    int M;
    while(scanf("%d",&n)==1){

        scanf("%d",&M);
        int x;
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            S[i]=S[i-1]+x;
        }
        int l=1;int r=1;
        f[0]=0;q[1]=0;
        for(int i=1;i<=n;i++){
            while(l<r&&Slope(q[l],q[l+1])<2*S[i])l++;
            int j=q[l];
            f[i]=f[j]+(S[i]-S[j])*(S[i]-S[j])+M;
            while(l<r&&Slope(q[r-1],q[r])>Slope(q[r],i))r--;
            q[++r]=i;
        }
        printf("%lld\n",f[n]);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章