HDU 斜率优化dp 3507 Print Article

来源:互联网 发布:中序线索树的递归算法 编辑:程序博客网 时间:2024/04/30 11:41

Print Article

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 4567    Accepted Submission(s): 1370


Problem Description
Zero has an old printer that doesn't work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate this degree.
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost

M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
 

Input
There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.
 

Output
A single number, meaning the mininum cost to print the article.
 

Sample Input
5 559575
 

Sample Output
230
 

Author
Xnozero
 

Source
2010 ACM-ICPC Multi-University Training Contest(7)——Host by HIT

题意:有很多个单词,每个单词有自己的费用,你需要为单词分配到任意行,使得费用最小,一行的费用是单词费用总和的平方+M。


思路:动态转移方程很容易想到是 dp[i]=min(dp[j]+(sum[i]-sum[j])^2)+M ,但是直接这样做毫无疑问是会超时的。 那么这个方程其实能让我们想到用斜率优化的。sum是单调的,我们对于k<j,假设j的决策更优,那么有

dp[j]+(sum[i]-sum[j])^2<dp[k]+(sum[i]-sum[j])^2;

dp[j]+sum[j]^2-(dp[k]+sum[k]^2)<2*sum[i]*(sum[j]-sum[k]) 设 dp[i]+sum[i]^2=y[i];

那么 (y[j]-y[k])/(sum[j]-sum[k]) < 2*sum[i]

设g[j,k]=(y[j]-y[k])/(sum[j]-sum[k]) 

那么 我们只需要维护g[i,j] 递增就行了。

不懂的,没关系,百度一下“斜率优化dp”会有很多的讲解,绝对能把你讲懂。


代码:

#include<iostream>#include<cstdio>#include<string.h>#include<cstring>#include<stdio.h>#include<stdlib.h>#include<queue>using namespace std;const int maxn = 500000 + 5;#define LL long longint N, M;LL dp[maxn];LL sum[maxn];int q[maxn];inline LL y(int i) { return dp[i] + sum[i] * sum[i]; }inline LL up(int i, int j) { return y(i) - y(j); }inline LL down(int i, int j) { return 2 * (sum[i] - sum[j]); }void input(){sum[0] = 0;for (int i = 1; i <= N; ++i) { scanf("%I64d", sum + i); sum[i] += sum[i - 1]; }}void solve(){int front = 0, rear = 0;q[rear++] = 0;for (int i = 1; i <= N; ++i){while (rear - front >= 2 && up(q[front + 1], q[front]) < sum[i] * down(q[front + 1], q[front]))++front;int j = q[front];dp[i] = dp[j] + (sum[i] - sum[j])*(sum[i] - sum[j]) + M;while (rear - front >= 2 && up(q[rear - 1], q[rear - 2])*down(i, q[rear - 1]) >= up(i, q[rear - 1])*down(q[rear - 1], q[rear - 2])) --rear;q[rear++] = i;}cout << dp[N] << endl;}int main(){while (scanf("%d%d", &N, &M) == 2){input();solve();}}


0 0
原创粉丝点击