【转】斜率优化的一道例题

我们知道,有些DP方程可以转化成DP[i]=f[j]+x[i]的形式,其中f[j]中保存了只与j相关的量。这样的DP方程我们可以用单调队列进行优化,从而使得O(n^2)的复杂度降到O(n)。

可是并不是所有的方程都可以转化成上面的形式,举个例子:dp[i]=dp[j]+(x[i]-x[j])*(x[i]-x[j])。如果把右边的乘法化开的话,会得到x[i]*x[j]的项。这就没办法使得f[j]里只存在于j相关的量了。于是上面的单调队列优化方法就不好使了。

这里学习一种新的优化方法,叫做斜率优化,其实和凸包差不多,下面会解释。

举例子说明是最好的!HDU 3507,很适合的一个入门题。http://acm.hdu.edu.cn/showproblem.php?pid=3507

大概题意就是要输出N个数字a[N],输出的时候可以连续连续的输出,每连续输出一串,它的费用是 “这串数字和的平方加上一个常数M”。

我们设dp[i]表示输出到i的时候最少的花费,sum[i]表示从a[1]到a[i]的数字和。于是方程就是:

dp[i]=dp[j]+M+(sum[i]-sum[j])^2;

很显然这个是一个二维的。题目的数字有500000个,不用试了,二维铁定超时了。那我们就来试试斜率优化吧,看看是如何做到从O(n^2)复杂度降到O(n)的。

分析:

我们假设k<j<i。如果在j的时候决策要比在k的时候决策好,那么也是就是dp[j]+M+(sum[i]-sum[j])^2<dp[k]+M+(sum[i]-sum[k])^2。(因为是最小花费嘛,所以优就是小于)

两边移项一下,得到:(dp[j]+num[j]^2-(dp[k]+num[k]^2))/(2*(num[j]-num[k]))<sum[i]。我们把dp[j]-num[j]^2看做是yj,把2*num[j]看成是xj。

那么不就是yj-yk/xj-xk<sum[i]么?   左边是不是斜率的表示?

那么yj-yk/xj-xk<sum[i]说明了什么呢?  我们前面是不是假设j的决策比k的决策要好才得到这个表示的?
如果是的话,那么就说明g[j,k]=yj-jk/xj-xk<sum[i]代表这j的决策比k的决策要更优。

关键的来了:现在从左到右,还是设k<j<i,如果g[i,j]<g[j,k],那么j点便永远不可能成为最优解,可以直接将它踢出我们的最优解集。为什么呢?

我们假设g[i,j]<sum[i],那么就是说i点要比j点优,排除j点。

如果g[i,j]>=sum[i],那么j点此时是比i点要更优,但是同时g[j,k]>g[i,j]>sum[i]。这说明还有k点会比j点更优,同样排除j点。

排除多余的点,这便是一种优化!

接下来看看如何找最优解。

设k<j<i。

由于我们排除了g[i,j]<g[j,k]的情况,所以整个有效点集呈现一种上凸性质,即k j的斜率要大于j i的斜率。

这样,从左到右,斜率之间就是单调递减的了。当我们的最优解取得在j点的时候,那么k点不可能再取得比j点更优的解了,于是k点也可以排除。换句话说,j点之前的点全部不可能再比j点更优了,可以全部从解集中排除。

于是对于这题我们对于斜率优化做法可以总结如下:

1,用一个单调队列来维护解集。

2,假设队列中从头到尾已经有元素a b
c。那么当d要入队的时候,我们维护队列的上凸性质,即如果g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。

3,求解时候,从队头开始,如果已有元素a b
c,当i点要求解时,如果g[b,a]<sum[i],那么说明b点比a点更优,a点可以排除,于是a出队。最后dp[i]=getDp(q[head])。

 1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 int dp[500005];
6 int q[500005];
7 int sum[500005];
8 int head,tail,n,m;
9
10 int getDP(int i,int j)
11 {
12 return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
13 }
14
15 int getUP(int j,int k) //yj-yk的部分
16 {
17 return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
18 }
19
20 int getDOWN(int j,int k) //xj-xk的部分
21 {
22 return 2*(sum[j]-sum[k]);
23 }
24
25 int main()
26 {
27 int i;
28 freopen("D:\\in.txt","r",stdin);
29 while(scanf("%d%d",&n,&m)==2)
30 {
31 for(i=1;i<=n;i++)
32 scanf("%d",&sum[i]);
33 sum[0]=dp[0]=0;
34 for(i=1;i<=n;i++)
35 sum[i]+=sum[i-1];
36 head=tail=0;
37 q[tail++]=0;
38 for(i=1;i<=n;i++)
39 {
40 while(head+1<tail && getUP(q[head+1],q[head])<=sum[i]*getDOWN(q[head+1],q[head]))
41 head++;
42 dp[i]=getDP(i,q[head]);
43 while(head+1<tail && getUP(i,q[tail-1])*getDOWN(q[tail-1],q[tail-2])<=getUP(q[tail-1],q[tail-2])*getDOWN(i,q[tail-1]))
44 tail--;
45 q[tail++]=i;
46 }
47 printf("%d\n",dp[n]);
48 }
49 return 0;
50 }

源自:http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html

时间: 2024-07-30 03:24:19

【转】斜率优化的一道例题的相关文章

Print Article hdu 3507 一道斜率优化DP 表示是基础题,但对我来说很难

Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 4990    Accepted Submission(s): 1509 Problem Description Zero has an old printer that doesn't work well sometimes. As it is antique

「斜率优化」解析及例题

前言 我们知道单调队列可以用来优化动态规划,当状态转移方程可以被表示为f[i] = (只与j有关的) + 一些常数 时便可以用单调队列来保存j来O(1)完成寻找j的过程,因此将$O(n^2)$优化为了$O(n)$ 那么如果当有一个转移方程变为了f[i] = (f[j] + 与i有关的)^2 ...这种情况时,我们发现展开的时候有f[j]*s[i]这类的项,因此不能再用单调队列直接来进行优化了,这时候就要用到斜率优化. 例题分析 空说没用,举例来讲吧: 传送门:>[HNOI2008]玩具装箱TOY

_bzoj1096 [ZJOI2007]仓库建设【斜率优化dp】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1096 又是一道经典斜率优化. #include <cstdio> const int maxn = 1000005; int n, c[maxn], head, tail, x[maxn]; long long sp[maxn], sxp[maxn], f[maxn]; char ch; struct point { long long x, y; int id; } que[maxn]

学渣乱搞系列之dp斜率优化

学渣乱搞系列之dp斜率优化 By 狂徒归来 貌似dp的斜率优化一直很难搞啊,尤其是像我这种数学很挫的学渣,压根不懂什么凸包,什么上凸下凸的,哎...说多了都是泪,跟wdd讨论了下,得出一些结论.本文很大部分参考了大神Accept的文章,不过此神貌似早已绝迹江湖,这篇文章写得好,也写得很差,前半部分叙述得很好,可是关键,关键部分说得很乱,有些许错误,很多大神都进行了评论指出,但是大神Accept貌似没有修改的意思,故重新总结下,以便自己以后查阅和复习啊. 下面看一个例题Print Article.

hdu 2993 MAX Average Problem (斜率优化dp入门)

MAX Average Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5855    Accepted Submission(s): 1456 Problem Description Consider a simple sequence which only contains positive integers as

【BZOJ-1597】土地购买 DP + 斜率优化

1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2931  Solved: 1091[Submit][Status][Discuss] Description 农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地

CDOJ 879 摩天轮 dp+斜率优化

原题链接:http://www.acm.uestc.edu.cn/#/problem/show/879 题意: 中文题 题解: 这是一道斜率dp的题. 先把$a$数组排个序. 令$dp[i][j]$表示第$i$个人坐在第$j$个箱子里面的最优解. 容易得到以下转移方程: $$dp[i][j]=min \left \{ dp[k][j-1]+(a[i]-a[k+1])^2 \right \}$$ 观察这个式子,发现好像可以斜率优化: 若$u>v$且$u$要比$v$优,即: $$dp[u][j-1]

【以前的空间】斜率优化的一点点总结

今天有看了一道dp题,发现好像裸不能过,应该是要斜率优化,结果发现自己那点傻×智商早把这东西忘得差不多,而且当时也是有点乱不是弄得很懂.于是又花了一个早上来整理下. <用单调性优化动态规划> 这个东西的话很好.但是由于我蒟蒻所以看不懂.先找了模板题到网上找题解,然后跟着题解自己推.那么我就用<[ZJOI2007]仓库建设>来推吧! 首先很容易写出状态转移方程 f[i]:=Min(f[j]+w[j,i])+c[i] 其中f[i]表示前i个的最优状态且在i建一个仓库,j表示上一个仓库的

斜率优化dp简讲 &amp;&amp; HDU 3507 Print Article

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 t