版权声明:博主的文章,请随意转载https://blog.csdn.net/Acer12138/article/details/82943079

题目标题:打印文章

给出N个单词,每个单词有个非负权值Ci,现在要将它们分成连续的若干段,每段的代价为此段单词的权值和的平方,还要加一个常数M,即。现在想求出一种最优方案,使得总费用之和最小。
HDU - 3507 , Print Article [ 斜率优化dp , 良心博客 ]-LMLPHP

输入格式

包含多组测试数据,对于每组测试数据。 第一行包含两个整数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

思路 :
其实看到这个题就是dp 而且递推方程式为
dp[i] = min { dp[i] ,dp[j] + (sum[i] - sum[j])*(sum[i] - sum[j]) + m }
但是复杂度一估计直接爆炸,明显用这个方程式写出来的是一个二维的 dp 因此需要优化

如果对于 k 来说有一个更加优化的 j 的话 那么 可以得到
dp[j] + ( sum[i] - sum[j] ) ^ 2 + m > dp[k] + (sum[i] - sum[k] ) ^ 2 + m ;
移项化简可得
(dp[j] - dp[k]) + (sum[j] ^ 2 - sum[k] ^ 2) < 2 * (sum[j] - sum[k]) * sum[i];
令 up = (dp[j] - dp[k]) + (sum[j] ^ 2 - sum[k] ^ 2) , down = 2 * (sum[j] - sum[k])
那么 sum[i] = up (j ,k ) / down(j ,k ) ;
sum[i] 是前缀和因此sum数组是递增的,因此这个题可以用斜率优化

先看代码 :

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 5e5+50;

typedef long long ll;

ll dp[maxn] ,sum[maxn] ,v[maxn] ;
int n ,m ;
int que[maxn] ,head ,tail ;

ll getdp(int i ,int j ) { // dp
	return dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]) + m;
}

ll getup(int j ,int k ) { // up
	return dp[j] - dp[k] + sum[j] * sum[j] - sum[k] * sum[k] ;
}

ll getdown(int j ,int k ) { // down
	return 2 * ( sum[j] - sum[k] );
}

int main() {
	while( ~scanf("%d %d" , &n ,&m ) ) {
		memset(dp ,0 ,sizeof(dp));
		for (int i = 1 ; i <= n ; i ++ ) {
			scanf ("%lld",&v[i]);
			sum[i] = sum[i-1] + v[i];
		}
		head = tail = 0; dp[tail ++] = 0; // 不可以从 1 开始进入队列,因为前缀和是从 0 开始的
		for (int i = 1 ; i <= n ; i ++ ) {
			while(head + 1 < tail && getup(que[head+1] ,que[head]) < getdown(que[head+1] ,que[head]) * sum[i] ) head ++ ;
			dp[i] = getdp(i ,que[head] ) ;
			while(head + 1 < tail && getup(que[tail-1] ,que[tail-2]) * getdown(i ,que[tail-1]) >= getup(i ,que[tail-1]) * getdown(que[tail-1] ,que[tail-2])) tail --;
			que[tail ++] = i;
		}
		printf("%lld\n",dp[n]);
	}
	return 0;
}

1 ) , 我们来看for循环里面的第一个 while 就相当于在que[head] 后面还有 比 que[head] 更加优化情况因此每次都要 head ++ ,属于这种情况 :
HDU - 3507 , Print Article [ 斜率优化dp , 良心博客 ]-LMLPHP
此时 i 为我们待求的 ,k 为que[ head ] , j 为 que[head + 1] 明显 j要比 k更加优化

2 ) , 对于第二个while 循环 可以这样理解 当前的 i 如果要加入队列的话必须保持队列的斜率是单调递增的
HDU - 3507 , Print Article [ 斜率优化dp , 良心博客 ]-LMLPHP
这种情况就是 i 是当前待加入的 , j 代表 que[tail-1] , k 代表que[tail-2] , 这种情况下 j 是明显不符合条件的因此要舍去 j , tail –

10-05 12:26