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


    DP颂

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

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

    所以dp还是很有的学的~

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

    单调队列优化dp

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


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

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

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

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

    但是如果是这样呢?

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

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

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

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

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

    其中不交易的情况好处理, 但是如果前面两种枚举(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 (kin[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);
    }
    

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

  • 相关阅读:
    c# 自定义事件和委托
    C#委托之个人理解(转)
    invokeRequired属性和 invoke()方法
    .NET(C#)连接各类数据库
    Mobile Web Development with ASP.NET 2.0
    移动飞信WEB发送服务接口
    4行C#代码打造专业数据库连接配置界面
    domino 中 UniversalID 和NoteID的区别
    VB中preserve的用法
    LOTUS Note ID 剖析
  • 原文地址:https://www.cnblogs.com/enzymii/p/8413686.html
Copyright © 2020-2023  润新知