笔记篇单调队列优化dp学习笔记&&luogu2569_bzoj1855股票交♂易

Posted Rabbit House~❤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了笔记篇单调队列优化dp学习笔记&&luogu2569_bzoj1855股票交♂易相关的知识,希望对你有一定的参考价值。

DP颂

DP之神 圣洁美丽 算法光芒照大地
我们怀着 崇高敬意 跪倒在DP神殿
你的复杂 能让蒟蒻 试图入门却放弃
在你光辉 照耀下面 AC真心不容易

dp大概是最经久不衰 亘古不化的算法了吧.
而且有各种各样的类型 优化之类的.
一直dp都不怎么好. 而且也不太知道应该怎么提高.
基本见到不认识的dp方程就不大会推(但我会打表啊= =

所以dp还是很有的学的~

正好最近刚刚肝了计算几何, 所以就顺带搞一下斜率优化dp一类的...

单调队列优化dp

单调队列大家都会吧?
不会的先出去学一下, 这里不讲.


好的, 我们来看一下这个柿子

\\[f[i]=max\\{f[j]+\\omega(j)\\} (j\\in[1..i)) \\]

其中\\(\\omega(j)\\)是一个费用函数, 一般会根据题目的不同而变化.

这个dp能做到什么复杂度呢?
首先一眼\\(O(n^2)\\)...
然而我们可以用一个变量记录一下之前出现过的最大值.
这样转移是\\(O(1)\\)的了, 总复杂度就降到了\\(O(n)\\).

但是如果是这样呢?

\\[f[i]=max\\{f[j]+\\omega(j)\\}(j\\in[i-m,i)) \\]

那就不能只维护一个变量了, 因为最大值如果出现在\\(j\\)的取值区间之外则转移是不合法的.
这样我们就考虑用单调队列来维护最大值, 这样转移依然可以做到\\(O(1)\\), 总复杂度\\(O(n)\\).

看道: (woc辣鸡bzoj给的什么zz数据范围, T都没给怎么做...)

这题可以写出这么一个状态转移方程

\\[令f[i][j]表示第i天拥有j支股票的最大收益, \\\\ f[i][j]=max\\left\\{ \\begin{matrix} f[i-w-1][k]-ap[i]*(j-k), (k\\in[j-as[i],j)) //买入\\\\ f[i-w-1][k]+bp[i]*(k-j),(k\\in(j,j+bs[i]]) //卖出\\\\ f[i-1][j]//不交♂易 \\end{matrix} \\right. \\]

其中不交易的情况好处理, 但是如果前面两种枚举\\(k\\)的话就要做到\\(O(n^*maxP^2)\\), 显然是过不了的, 我们必须考虑优化.
我们以买入为栗化一波柿子(因为卖出同理) :

\\[f[i][j]=max\\{f[i-w-1][k]-ap[i]*(j-k)\\}\\\\ =max\\{f[i-w-1][k]+ap[i]*k\\}-ap[i]*j (k\\in[j-as[i],j]) \\]

我们令\\(\\omega(x)=ap[i]*x\\), 而我们枚举\\(i\\), 就可以视为\\(i\\)是定值, 于是\\(ap[i],as[i]\\)都是定值.
我们就可以看出第二维形成了一个能用单调队列优化的柿子了.
这样优化之后复杂度成功降到了\\(O(n*maxP)\\), 就可以通过此题了.

根据贪心原则, 为了获得最多的现金, 手里不应该留股票, 所以用每个\\(f[i][0]\\)更新答案即可.

不过要注意一下边界条件... 挺扯淡的..

代码:

#include <cstdio>
#include <cstring>
const int N=2020;
const int INF=0x7fffffff;
int q[N<<1],d[N<<1],h=1,t=0;
int f[N][N],ap[N],bp[N],as[N],bs[N];
inline int gn(int a=0,char c=0){
	for(;c<\'0\'||c>\'9\';c=getchar());
	for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
inline int max(const int& a,const int& b){
	return a>b?a:b;
}
int main(){
	int n=gn(),m=gn(),w=gn(),ans=0;
	for(int i=1;i<=n;++i)
		ap[i]=gn(),bp[i]=gn(),as[i]=gn(),bs[i]=gn();
	memset(f,192,sizeof(f));
	for(int i=1;i<=n;++i){
		for(int j=0;j<=as[i];++j) f[i][j]=-ap[i]*j;
		for(int j=0;j<=m;++j) f[i][j]=max(f[i][j],f[i-1][j]);
		if(i>w){
				h=1; t=0;
			for(int j=0;j<=m;++j){				
				int val=f[i-w-1][j]+j*ap[i];
				while(t>=h&&val>=q[t]) --t;
				q[++t]=val; d[t]=j;
				while(t>=h&&d[h]<j-as[i]) ++h;
				f[i][j]=max(f[i][j],q[h]-ap[i]*j);
			}
			h=1; t=0;
			for(int j=m;j>=0;--j){
				int val=f[i-w-1][j]+bp[i]*j;
				while(t>=h&&val>=q[t]) --t;
				q[++t]=val; d[t]=j;
				while(t>=h&&d[h]>j+bs[i]) ++h;
				f[i][j]=max(f[i][j],q[h]-bp[i]*j);
			}
		}
		ans=max(ans,f[i][0]);
	}
	printf("%d",ans);
}

反正差不多就这样吧....

以上是关于笔记篇单调队列优化dp学习笔记&&luogu2569_bzoj1855股票交♂易的主要内容,如果未能解决你的问题,请参考以下文章

7.14 单调栈 单调队列 +dp优化

单调队列优化DP

学习笔记::单调栈

单调性优化学习笔记

FCS NOI2018福建省冬摸鱼笔记 day4

Python学习笔记——进阶篇第八周———Socket编程进阶&多线程多进程