hdu3401 Trade [单调队列优化dp]

本文介绍如何使用单调队列技术优化股票买卖策略,通过动态规划与单调队列结合,实现对股票买卖时间差限制下的最大利润计算。详细阐述了DP方程的优化过程,并提供了关键代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单调队列:是在一个队列中选出的一些元素构成的队列

正因为单调队列能够将一些无用的店删去并且保持了第一个数就是最值

所以我们可以将一些求么某个区间的最值转化成单调队列来做



单调队列优化:

嗯 不知道从哪里盗来的图(很感谢原作者)

这张图能很好的说说明在什么样的情况下去优化

对于图中的dp[i][j]他可以由f[i-1][j-c]到d[i-1][j-1]某一个最值,这里假设最大值;

那么在对于下一个数dp[i][j+1] 他的区间只是往后推了 所以有部分区间是重复的 可以将重复区间进行记录,这个正好是单调队列所拥有的性质

好了回到这倒题本身,这个题是说我们经行股票的买卖,但是我们的每一次的交易与上一次的交易的时间差是不能小于w

求最大获利

嗯 我们可以很快速的想到这道题的dp方程为 dp[i][j]=max{dp[i-1][j](不进行买卖),dp[i-w-1][k]-(j-k)*a[i],dp[i-w-1][k]+(k-j)*b[i]};

很显然这个是n三次转移方程

但是我们对于产生第三个n进行优化,将我们得到的式子进行化简,所以dp[i][j]=dp[i-w-1][k]+k*a[i]-j*a[j],其实我们就是对于每一个i,j求dp[i-w-1][k]+k*a[i]的最大值

所以我们可是令f[i][k]=dp[i-w-1][k]+k*a[i]

对于卖出也是同买入的情况


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct Node{
	int x,y;
}a[2001],b[2001];
struct Node1{
	int x,t;
}s[2001];
int dp[2001][2001];
int main(){
	int T,n,m,w;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d",&n,&m,&w);
		//memset(dp,0,sizeof(dp));
		for(int j=1;j<=n;j++){
			scanf("%d%d%d%d",&a[j].x,&a[j].y,&b[j].x,&b[j].y);
		for(int i=0;i<=m;i++)
			dp[j][i]=-1234567890;  
		}	
	       	for(int i=1; i<=w+1; ++i)  
            		for(int j=0; j<=b[i].x; ++j)  
               			 dp[i][j]=(-a[i].x*j);  
		for(int i=2;i<=n;i++){
			int l=0,r=-1;
			s[0].x=0;
			s[0].t=0;
			for(int j=0;j<=m;j++)
				dp[i][j]=max(dp[i][j],dp[i-1][j]);
		
			if(i-w-1>0){
			for(int j=0;j<=m;j++){
				int tmp=dp[i-1-w][j]+j*a[i].x;
				while(r>=l&&s[r].x<=tmp) r--;
				r++;
				s[r].x=tmp;
				s[r].t=j;
				while(s[l].t<j-b[i].x) l++;
				dp[i][j]=max(dp[i][j],s[l].x-j*a[i].x);
			//	cout << l <<"****" << r << endl;
			//	for(int z=l;z<=r;z++)
		//			cout << s[z].x << " " << s[z].t  << endl;
			}
			l=0,r=-1;
			s[0].x=0;
			s[0].t=0;
				for(int j=0;j<b[i].y;j++){	
					int tmp=dp[i-1-w][j]+j*a[i].y;
					while(r>=l&&s[r].x<=tmp) r--;
					r++;
					s[r].x=tmp;
					s[r].t=j;	
				}
				for(int j=0;j<=m;j++){
					if(j+b[i].y<=m){ 
					int tmp=dp[i-1-w][j+b[i].y]+(j+b[i].y)*a[i].y;
					while(r>=l&&s[r].x<=tmp) r--;
					r++;
					s[r].x=tmp;
					s[r].t=j+b[i].y;
					}
					while(s[l].t<j) l++;
					dp[i][j]=max(dp[i][j],s[l].x-j*a[i].y);
			}	
			}
		}
			int ans=0;
			for(int i=0;i<=m;i++)
				ans=max(ans,dp[n][i]);
			printf("%d\n",ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值