hdu3507 Print Article
Print Article
【题目标题】:
打印文章
给出N个单词,每个单词有个非负权值Ci,现在要将它们分成连续的若干段,每段的代价为此段单词的权值和的平方,还要加一个常数M,即。现在想求出一种最优方案,使得总费用之和最小。
【输入格式】
包含多组测试数据,对于每组测试数据。 第一行包含两个整数N和M(0<=N<=500000,0<=M<=1000)。 第2-N+1行为N个整数。
【输出格式】
输出仅一个整数,表示最小的价值。
【样例输入】
5 5
5
9
5
7
5
3 0
1
2
3
【样例输出】
230
14
sol:题目中特意把n≤500000标红意思是说n2绝对凉凉
对于我这种菜鸡,只会先把暴力打出来再看
k
对于前缀和Qzhi=∑ Ci
i=1
dp[i]=min(dp[i],dp[j]+(Qzh[i]-Qzh[j])2+m)
似乎我只会这样就完了(题解大法好!!!)
斜率优化dp
dp[i]=dp[j]+Qzh[i]2-2*Qzh[i]*Qzh[j]+Qzh[j]2+m
对于j<k<i,如果k比j要优
则满足 dp[k]+Qzh[i]2-2*Qzh[i]*Qzh[k]+Qzh[k]2+m ≤ dp[j]+Qzh[i]2-2*Qzh[i]*Qzh[j]+Qzh[j]2+m
消去一样的可得 dp[k]-2*Qzh[i]*Qzh[k]+Qzh[k]2 ≤ dp[j]-2*Qzh[i]*Qzh[j]+Qzh[j]2
移项可得 dp[k]+Qzh[k]2-dp[j]-Qzh[j]2 ≤ 2*Qzh[i]*(Qzh[k]-Qzh[j])
所以可知 当(j<k<i) dp[k]+Qzh[k]2-dp[j]-Qzh[j]2 ≤ 2*Qzh[i]*(Qzh[k]-Qzh[j])时 k 比 j 优
设 G[j][k]表示 G[j,k]=((dp[k]+Qzh[k]2)-(dp[j]+Qzh[j]2))/(2*Qzh[i]*(Qzh[k]-Qzh[j]))
当G[j][k]<=1 k优于j,否则j优于k (其实G[j][k]就是斜率)
当(j<k<i) 当G[j][k]>=G[k][i],k就没用了
/* //dp[i]表示输出到第i位的最小费用 //因此很容易推出dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j])^2+m) //但是复杂度为O(n^2),一定会超时, //假设j<k,假设k优于j //dp[k]+(sum[i]-sum[k])^2+m<dp[j]+(sum[i]-sum[j])^2+m //G[j,k]=((dp[k]+sum[k]^2)-(dp[j]+sum[j]^2))/(2*sum[i]*(sum[k]-sum[j]))<=1时 //假设成立,也就是k优于j,否则j优于k //当k1<k<k2时,G[k1,k]>=G[k,k2]时,k没有用处 */ #include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=500005; int n,m; int Queue[N]; ll Qzh[N],dp[N]; #define Sqr(x) ((x)*(x)) inline bool Pand(int j,int k,int i) //j<k<i { ll P1=dp[k]+Sqr(Qzh[k])-dp[j]-Sqr(Qzh[j]); ll P2=2*Qzh[i]*(Qzh[k]-Qzh[j]); return (P1<=P2)?(1):(0); } inline bool Pand_Rev(int j,int k,int i) //j<k<i { ll P1=(dp[k]+Sqr(Qzh[k])-dp[j]-Sqr(Qzh[j]))*(Qzh[i]-Qzh[k]); ll P2=(dp[i]+Sqr(Qzh[i])-dp[k]-Sqr(Qzh[k]))*(Qzh[k]-Qzh[j]); return (P1>=P2)?(1):(0); } int main() { int i,j; while(~scanf("%d%d",&n,&m)) { for(i=1;i<=n;i++) { Qzh[i]=Qzh[i-1]+read(); } int Head=0,Tail=0; Queue[0]=0; for(i=1;i<=n;i++) { while(Head<Tail&&Pand(Queue[Head],Queue[Head+1],i)) Head++; dp[i]=dp[Queue[Head]]+Sqr(Qzh[i]-Qzh[Queue[Head]])+m; while(Head<Tail&&Pand_Rev(Queue[Tail-1],Queue[Tail],i)) Tail--; Queue[++Tail]=i; } Wl(dp[n]); } return 0; } /* input 5 5 5 9 5 7 5 3 0 1 2 3 output 230 14 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!