斜率优化dp 的简单入门
2018-08-28 21:40
726 查看
不想写什么详细的讲解了...而且也觉得自己很难写过某大佬(大米饼),于是建议把他的 blog 先看一遍,然后自己加了几道题目以及解析...顺便建议看看算法竞赛(蓝皮书)的 0x5A 斜率优化(P294) 部分
[b]这是——大米饼大佬[/b]
看完了大米饼同志对斜率优化的介绍,下面我来稍微讲讲对斜率优化dp 的理解
平面直角坐标系
直线解析式
等式处理
dp状态设计
balabala......
根据题目(斜率优化 dp 的题目一般都很裸)的 题意 以及 数据范围 先根据设计一个状态出来,当做 dp 数组。
根据已经设计好的 dp 状态设计一个状态转移方程,如果设计失败则跳回第一步,重新设计 dp 状态。
在第二步完成后,我们一般可以得到这样的一个式子:$ Fi = Fj +$ $(一个表达式)$
根据设计的状态做一些乱七八糟的东西(比如说将式子展开啦,移项啦...这些大米饼讲的应该挺清楚的吧)。
在第四步中,我们在将式子展开后,把下标中同时带有 i、j 的项(注意这里是项,如 Si*Sj)移到等式的左边。
然后我们就可以得到一个 $b + k x = y$ 形态的一般式(大米饼是这么叫的),其中等式左边下标带有 i 的变量(注意这里是变量,如Si)作为 k ,下标带有 j 的变量作为 x ,等式右边下标带有 j 的变量作为 y ,至于等式右边那些下标只带有 i 的变量我们不去管它,因为 i 在当前对于 Fi 的计算中相当于一个常量,对结果的影响一定。
然后我们发现这时候我们就可以得到点 (x, y) 了,那么我们就基本上已经完成了这道 斜率优化 dp 题了。
那么我们是要根据题意让 Fi (也就是一般式中的 b )取到最小(大)值,那么我们的任务也就转化成了过一个点,画一条斜率为 k 的射线,令其与 y 轴的截距(与 y 轴的交点和原点的距离叫做截距)最小(大)。
做完这些工作之后,你把所有的点画到平面直角坐标系上,就可以看到这样的一条折线(假设我们现在求的是 Fi 的最小值):
没错,这就是可能有用的点形成的像凸包一样的东西(凸壳)。那为什么这些点形成的折线一定是向下凸起的呢?
我们可以想想一下,如果折线内凹,那么引起内凹的那个点可能成为有用点吗?不可能,因为经过该点的斜率为 k 的直线与 y 轴的截距必然不会比它旁边两个点的截距要小
那么我们在来考虑一下,如果我们要求的是 Fi 的最大值呢?那么我们只要让折线向上凸起就好了(维护上凸性)。
对了对了!还有一点蛮重要的。那就是斜率优化可行性判定的标准(我自己口胡的): 我们在上面的步骤中会处理出一个一般式中的 k 吗?那个 k 要满足单调性,
不然是没办法用双端队列(单调队列)维护的。
然后这里有一些题目
这道题你只要一直推推推把式子推出来,然后发现 $ans = m * \sum_{i=1}^{m}a[i]^{2} - sum
^{2}$ ,
那么你就能知道我们要求的是 $min{\sum_{i=1}^{m}a[i]^{2}}$ (这就是序列分割啊),最后答案处理一下就好了。
洛谷P4360 [CEOI2004]锯木厂选址
这题偷懒一点就是 抄 综合 一下序列分割这道题,将它作为模板,K 设成 3 ,n 要加一,然后常规做就好了。推式子也不是很麻烦,和仓库建设一样的套路
其他题...咳咳(来自刷题慢者的尴尬...做一道题都要在黑板上墨迹个半天QwQ)
[b]这是——大米饼大佬[/b]
看完了大米饼同志对斜率优化的介绍,下面我来稍微讲讲对斜率优化dp 的理解
前置知识
单调队列平面直角坐标系
直线解析式
等式处理
dp状态设计
balabala......
理解
其实斜率优化 dp 的原理很简单:根据题目(斜率优化 dp 的题目一般都很裸)的 题意 以及 数据范围 先根据设计一个状态出来,当做 dp 数组。
根据已经设计好的 dp 状态设计一个状态转移方程,如果设计失败则跳回第一步,重新设计 dp 状态。
在第二步完成后,我们一般可以得到这样的一个式子:$ Fi = Fj +$ $(一个表达式)$
根据设计的状态做一些乱七八糟的东西(比如说将式子展开啦,移项啦...这些大米饼讲的应该挺清楚的吧)。
在第四步中,我们在将式子展开后,把下标中同时带有 i、j 的项(注意这里是项,如 Si*Sj)移到等式的左边。
然后我们就可以得到一个 $b + k x = y$ 形态的一般式(大米饼是这么叫的),其中等式左边下标带有 i 的变量(注意这里是变量,如Si)作为 k ,下标带有 j 的变量作为 x ,等式右边下标带有 j 的变量作为 y ,至于等式右边那些下标只带有 i 的变量我们不去管它,因为 i 在当前对于 Fi 的计算中相当于一个常量,对结果的影响一定。
然后我们发现这时候我们就可以得到点 (x, y) 了,那么我们就基本上已经完成了这道 斜率优化 dp 题了。
那么我们是要根据题意让 Fi (也就是一般式中的 b )取到最小(大)值,那么我们的任务也就转化成了过一个点,画一条斜率为 k 的射线,令其与 y 轴的截距(与 y 轴的交点和原点的距离叫做截距)最小(大)。
做完这些工作之后,你把所有的点画到平面直角坐标系上,就可以看到这样的一条折线(假设我们现在求的是 Fi 的最小值):
没错,这就是可能有用的点形成的像凸包一样的东西(凸壳)。那为什么这些点形成的折线一定是向下凸起的呢?
我们可以想想一下,如果折线内凹,那么引起内凹的那个点可能成为有用点吗?不可能,因为经过该点的斜率为 k 的直线与 y 轴的截距必然不会比它旁边两个点的截距要小
那么我们在来考虑一下,如果我们要求的是 Fi 的最大值呢?那么我们只要让折线向上凸起就好了(维护上凸性)。
对了对了!还有一点蛮重要的。那就是斜率优化可行性判定的标准(我自己口胡的): 我们在上面的步骤中会处理出一个一般式中的 k 吗?那个 k 要满足单调性,
不然是没办法用双端队列(单调队列)维护的。
然后这里有一些题目
题目
洛谷P4072 [SDOI2016]征途
分析
首先你在做这题之前最好已经做过了 洛谷P3648 [APIO2014]序列分割 这道题(上面的 T6)。这道题你只要一直推推推把式子推出来,然后发现 $ans = m * \sum_{i=1}^{m}a[i]^{2} - sum
^{2}$ ,
那么你就能知道我们要求的是 $min{\sum_{i=1}^{m}a[i]^{2}}$ (这就是序列分割啊),最后答案处理一下就好了。
代码
//by Judge #include<iostream> #include<cstdio> #define ll long long using namespace std; const int M=3111; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) char buf[1<<21],*p1=buf,*p2=buf; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } ll n,m,sum[M],g[M],f[M],q[M],head,tail; inline double Rate(ll i,ll j){ if(sum[i]==sum[j]) return -1e9; return (g[j]-g[i]+sum[j]*sum[j]-sum[i]*sum[i])/(1.0*sum[j]-sum[i]); } signed main(){ n=read(),m=read(); for(int i=1;i<=n;++i) sum[i]=sum[i-1]+read(),g[i]=sum[i]*sum[i]; for(int k=2,i;k<=m;++k){ head=tail=0; for(i=1;i<=n;++i){ while(head<tail && Rate(q[head+1],q[head])<=2*sum[i]) ++head; f[i]=g[q[head]]+(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]]); while(head<tail && Rate(q[tail-1],q[tail])>=Rate(q[tail],i)) --tail; q[++tail]=i; } swap(f,g); } printf("%lld\n",g *m-sum *sum ); return 0; }
洛谷P4360 [CEOI2004]锯木厂选址
分析
首先这道题其实与仓库建设类似,同时(可以算是)综合了序列分割...但是数据的处理有点麻烦(甚至还有点坑,比如 d 和 w 搞反了然后样例里面 d 、w 读入雷同...)这题偷懒一点就是 抄 综合 一下序列分割这道题,将它作为模板,K 设成 3 ,n 要加一,然后常规做就好了。推式子也不是很麻烦,和仓库建设一样的套路
代码
//by Judge #include<iostream> #include<cstdio> #define int long long #define ll long long using namespace std; const int M=5e4+111; #ifdef ONLINE_JUDGE #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) #endif char buf[1<<21],*p1=buf,*p2=buf; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } ll n,K,x[M],w[M],s[M],g[M],f[M],q[M],head,tail; inline double Rate(ll i,ll j){ if(w[i]==w[j]) return -1e18; return (g[j]-g[i]+s[j]-s[i])/(1.0*w[j]-w[i]); } int las[M]; signed main(){ n=read()+1; for(int i=1;i<=n;++i) (i<n)&&(w[i]=read(),x[i+1]=x[i]+read()), s[i]=s[i-1]+x[i]*w[i],w[i]+=w[i-1],g[i]=1e18; for(int k=1,i;k<=3;++k){ head=tail=0; for(i=1;i<=n;++i){ while(head<tail && Rate(q[head+1],q[head])<=x[i]) ++head; f[i]=g[q[head]]+x[i]*(w[i-1]-w[q[head]])-s[i-1]+s[q[head]]; while(head<tail && Rate(q[tail-1],q[tail])>=Rate(q[tail],i)) --tail; q[++tail]=i; } swap(f,g); } printf("%lld\n",g ); return 0; }
其他题...咳咳(来自刷题慢者的尴尬...做一道题都要在黑板上墨迹个半天QwQ)
相关文章推荐
- 斜率优化dp 的简单入门
- Hdu 3507 Print Article【斜率优化Dp入门】
- hdu3507 Print Article[斜率优化dp入门题]
- HDU 3507 Print Article DP(斜率优化入门)
- 斜率优化DP习题集粹——从入门到放弃
- hdu 3507 斜率优化DP入门题
- dp day 4 斜率优化(fa)bzoj1597+简单dp问题
- hdu 2993 MAX Average Problem (斜率优化dp入门)
- hdu 2993 MAX Average Problem(DP+斜率优化入门题)
- hdu 3507 斜率优化dp入门
- Bzoj 1010[HNOI2008]玩具装箱toy【斜率优化Dp入门】
- hdu 3507 斜率优化dp 入门学习
- hdu 2993 简单斜率优化dp 学了一手变态输入法
- [hdu3507 Print Article]斜率优化dp入门
- 【HDU3507】Print Article-DP斜率优化入门
- hdu3570, 超级简单的斜率优化dp
- hdu3507之斜率优化DP入门
- 斜率优化DP入门(填坑)
- SDOI2016 R1 day2 T3 征途 斜率优化DP
- BZOJ1597(Usaco2008 Mar)[土地购买]--斜率优化DP