斜率DP hdu 3507

Posted 缄默火

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斜率DP hdu 3507相关的知识,希望对你有一定的参考价值。

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 5 5 9 5 7 5
 

 

Sample Output
230
 

 

Author
Xnozero
 

 

Source

大致题意:要打印一长串词语,每个词语有一个对应的打印费用Ci,要给词语分行,一行的总费用记为,M是给定的常数

要求计算一种分行方案使得总费用最小。

数据规模50万。

 

分析:首先可以想到枚举上一次断行处,这样可以得到最初的状态转移方程:

 

,复杂度为O(n^2)。

观察一下数据规模为50万,需要优化。

主要思路是考虑淘汰肯定对最优答案没有贡献的点。

 将状态转移方程展开:

(注:公式和图来自BIG YAO学长)

 

移项可得:

                    

观察到蓝色字体部分只与j有关,绿色字体对给定的i为常量,红色字体部分取最小的时候dp[i]取最小。

将蓝色字体视为y(j),sum[j]视为x(j),问题就转化为对平面上无数个点(x,y),对每一个i,找出一个最优点(x0,y0),使得一条通过该点,斜率为k=2sum[i]的直线的截距最小。

放张图表现一下优化情况:

维护一个队列,即为下凸折线上点的队列,每次寻找最优的j的时候只在队列里的点找。(注意取得最优点的时候相邻的两根折线的斜率对于k=2sum[i]一大一小)

以下具体讨论怎么实现:

i不断向前推进,每次循环做两件事情:

1,找出对于dp[i]最优的上一个断行处j

如果队列上该点i和他后面的那个店形成的斜率小于k=2*sum[i]就头指针+1。

注意由于随i的递增,k=2*sum[i]必然递增,所以出队的点就不需要回来了

2,把i放入队列后就不再需要的点淘汰掉:

一旦出现3个点呈这样,即可淘汰点2,因为:直线经过点4的截距必然小于经过点2的截距,而经过点4的截距必然小于经过点1或点3的截距。

从而经过点2的截距必然小于小1或点3的截距,点2不可能为最优点,可淘汰。

 

然后把i放入队列(注意sum[i]为严格递增的,所以i个点中最后一个点必然在“外围”,不会被淘汰)

 

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,a,b) for(int i=a;i<=b;++i)
 4 using namespace std;
 5 const int MAXN=500010;
 6 long long int dp[MAXN],q[MAXN],sum[MAXN];
 7 int n,m;
 8 inline long long int getdp(int i,int j)
 9 {
10     return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
11 }
12 inline long long int gety(int x,int y)
13 {
14     return dp[x]+sum[x]*sum[x]-dp[y]-sum[y]*sum[y];
15 }
16 inline long long int getx(int x,int y)
17 {
18     return 2*sum[x]-2*sum[y];
19 }
20 int main()
21 {
22    // freopen("in.txt","r",stdin);
23     while(scanf("%d%d",&n,&m)==2)
24     {
25         sum[0]=0;
26         rep(i,1,n)
27         {
28             scanf("%lld",&sum[i]);
29             sum[i]=sum[i-1]+sum[i];
30         }
31         dp[0]=0;
32         int head,tail;
33         tail=0;
34         head=0;
35         q[tail]=0;          //虚拟制造一个点0,若0点最优代表把所有词语分成一行最优
36         rep(i,1,n)
37         {
38             while(head+1<=tail&&(gety(q[head+1],q[head])<=sum[i]*getx(q[head+1],q[head]))) head++;  //寻找对于i最好的上一次分行的截止点
39             dp[i]=getdp(i,q[head]);         
40             while(head+1<=tail&&gety(i,q[tail])*getx(q[tail],q[tail-1])<=gety(q[tail],q[tail-1])*getx(i,q[tail])) tail--;      //i放入队列后需要淘汰的点
41             q[++tail]=i;        //把i放入队列
42         }
43         printf("%lld\\n",dp[n]);
44     }
45     return 0;
46 }

 

以上是关于斜率DP hdu 3507的主要内容,如果未能解决你的问题,请参考以下文章

[hdu 3507]斜率优化dp

HDU 3507 斜率优化dp

HDU 3507斜率优化dp

HDU 3507 Print Article(斜率DP优化)

DP(斜率优化):HDU 3507 Print Article

hdu3507,斜率优化dp