题意
给出了一个费用计算公式,现在要你把一个数列分成若干组,每一组分别套用给出的费用计算公式计算费用。你要设计一种方法使得总费用最小。
思路
本题的DP方程推导相对容易(不过我学艺不精被卡在了这一步),之后我们会发现导出的方程完全无法满足时间限制,这个时候通过对费用公式的变形可以导出一种适用于斜率优化DP的形式。
斜率优化的操作非常复杂,幸运的是已经有人写出了详实的教程:斜率优化DP。这篇教程基本是斜率优化教程中最好理解的了,并且以这个题为例题。建议学习时手动模拟操作过程,以便形象、深入地理解。
代码
实现斜率优化的要点:1.合理正确地推导出斜率的表示形式和所有的转移方程、决策判定条件。2.正确地选择大于小于号。3.单调队列的实现,同时注意单调队列无法使用STL很好地实现,只能数组模拟。4.正确地初始化DP数组(非常重要)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
// // HDU3507.cpp // playground // // Created by Adam Chang on 2015/07/30. // Copyright © 2015年 Adam Chang. All rights reserved. // #include <iostream> #include <algorithm> #include <queue> #include <vector> #include <cstdlib> #include <cstdio> #include <string> #include <cstring> #include <ctime> #include <iomanip> #include <cmath> #include <set> #include <stack> #include <cmath> #include <map> using namespace std; #define MAXN 500000 + 10 long long sum[MAXN]; long long dp[MAXN]; int que[MAXN]; long long slope_upper(int a,int b){ return (dp[a] + sum[a] * sum[a]) - (dp[b] + sum[b] * sum[b]); } long long slope_lower(int a,int b){ return 2 * (sum[a] - sum[b]); } int main(){ long long n = 0, m = 0; while (scanf(" %lld %lld",&n,&m) != EOF) { int head = 0, tail = 0; que[tail++] = 0; for (int i = 1; i <= n; i++) { scanf(" %lld",&sum[i]); sum[i] += sum[i-1]; while (head + 1 < tail && slope_upper(que[head + 1], que[head]) <= sum[i] * slope_lower(que[head + 1], que[head])) { head++; } dp[i] = dp[que[head]] + m + (sum[i] - sum[que[head]]) * (sum[i] - sum[que[head]]); while (head + 1 < tail && slope_upper(i, que[tail - 1]) * slope_lower(que[tail - 1], que[tail - 2]) <= slope_upper(que[tail - 1], que[tail - 2]) * slope_lower(i, que[tail - 1])) { tail--; } que[tail++] = i; } printf("%lld\n",dp[n]); } return 0; } |