hdu 3507 Print Article

来源:互联网 发布:linux设置待机时间 编辑:程序博客网 时间:2024/06/08 05:37

Problem

acm.hdu.edu.cn/showproblem.php?pid=3507

vjudge.net/contest/76380#problem/A

Reference

www.cnblogs.com/kuangbin/archive/2012/08/26/2657650.html

www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html(这博客有些写错,应跟上面那个对比着看)

Meaning

有 n 个单词,每个单词有个 c[i] 值,把一段连续的单词 [ L , R ] 打印在同一行的花费是:

{ c[i] ^ 2 | L <= i <= R } + M(M是给定的常数)

要求吧所有单词都打印出来的最小花费总和。

Analysis

定义状态:dp[i]:打印前 i 个单词的最小花费

状态转移:dp[i] = min { dp[j] + sum[j, i] ^ 2 + M | 1 <= j <= i }(sum[i, j] = c[j] + c[j+1] + … + c[i])

这样的 DP 是 O(n^2)的,要优化,于是就用到参考博客说的斜率优化。

Code

#include <cstdio>using namespace std;const int M = 1000, N = 500000;int sum[N+1]; // c[i] 的前缀和int que[N+1]; // 单调队列int dp[N+1];/* 分子 */inline int up(int l, int f){return dp[l] + sum[l] * sum[l] - dp[f] - sum[f] * sum[f];}/* 分母 */inline int down(int l, int f){return sum[l] - sum[f] << 1;}inline int cal(int i, int j, int m){return dp[j] + (sum[i]-sum[j]) * (sum[i]-sum[j]) + m;}int main(){int n, m;while(~scanf("%d%d", &n, &m)){int head = 0, tail = 0;que[tail++] = sum[0] = dp[0] = 0;for(int i=1, in; i<=n; ++i){scanf("%d", &in);sum[i] = sum[i-1] + in;}for(int i=1; i<=n; ++i){// 找当前的最优取值while(head+1 < tail &&up(que[head+1], que[head]) <=sum[i] * down(que[head+1], que[head]))++head;dp[i] = cal(i, que[head], m);// 维护下凸性while(head+1 < tail &&up(i, que[tail-1]) * down(que[tail-1], que[tail-2]) <=up(que[tail-1], que[tail-2]) * down(i, que[tail-1])) --tail;que[tail++] = i;}printf("%d\n", dp[n]);}return 0;}

0 0