【题解】HDU3401 Trade 单调dp

你是一个股神,可以预测未来n天股票的价格,你最多持有m个股票,且每次交易之后需要间隔w天再进行交易。给定未来n天的每天股票的买入价格pi,卖出价格po,买入限制mi,卖出价值mo,假设初始资金无限,求最多能赚多少钱。

DP
状态表示: d p [ i ] [ j ] dp[i][j] dp[i][j]表示第i天持有j股票时最多赚多少钱.
状态边界: d p [ i ] [ j ] = − i n f , d p [ 0 ] [ 0 ] = 0. dp[i][j]=-inf, dp[0][0]=0. dp[i][j]=inf,dp[0][0]=0.
状态转移:

  1. 不买不卖, d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]
  2. 买入, d p [ i ] [ j ] = d p [ i − w − 1 ] [ k 1 ] − ( j − k 1 ) ∗ p i dp[i][j] = dp[i-w-1][k1]-(j-k1)*pi dp[i][j]=dp[iw1][k1](jk1)pi,其中 m a x ( 0 , j − m i ) &lt; = k 1 &lt; = j max(0,j-mi)&lt;=k1&lt;=j max(0,jmi)<=k1<=j
  3. 卖出, d p [ i ] [ j ] = d p [ i − w − 1 ] [ k 2 ] + ( k 2 − j ) ∗ p o dp[i][j] = dp[i-w-1][k2]+(k2-j)*po dp[i][j]=dp[iw1][k2]+(k2j)po,其中 j &lt; = k 2 &lt; = m i n ( m , j + m o ) j&lt;=k2&lt;=min(m,j+mo) j<=k2<=min(m,j+mo)

单调优化
d p [ i ] [ j ] = m a x ( d p [ i − w − 1 ] [ k 1 ] + k 1 ∗ p i ) − j ∗ p i dp[i][j]=max(dp[i-w-1][k1]+k1*pi)-j*pi dp[i][j]=max(dp[iw1][k1]+k1pi)jpi
d p [ i ] [ j ] = m a x ( d p [ i − w − 1 ] [ k 1 ] + k 2 ∗ p o ) − j ∗ p o dp[i][j]=max(dp[i-w-1][k1]+k2*po)-j*po dp[i][j]=max(dp[iw1][k1]+k2po)jpo

代码

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 2048, MOD = 1000000007;

int dp[M][M]; //dp[i][j]表示第i天持有j股票时的最大收益
int A[M], B[M]; //单调队列
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int T = read();
	while(T--)
	{
		memset(dp, 0xc0, sizeof(dp)); //负的极小值
		int n=read(), m=read(), w=read(); //天数, 最大钱数, 间隔

		// 初始状态
		dp[0][0] = 0;
		for(int i=1; i<=n; ++i)
		{
			int pi=read(), po=read(), mi=read(), mo=read(); 
			for(int j=0; j<=m; ++j)
				dp[i][j] = dp[i-1][j]; //不买不卖
			if(i<=w+1) 
			{
				for(int j=0; j<=mi; ++j)
					dp[i][j] = max(dp[i-1][j], -j*pi); //只能买
				continue;
			}		
			int l=0, r=0; //单调队列的队头与队尾(左闭右开)	
			for(int j=0; j<=m; ++j) //买入转移到现在
			{
				int val = dp[i-w-1][j] + j*pi;
				while(l<r && B[r-1]<val) r--;
				A[r]=j; B[r++]=val;
				while(l<r && A[l]+mi<j) l++;
				dp[i][j] = max(dp[i][j], B[l]-j*pi);
			}
			l = r = 0;
			for(int j=m; j>=0; --j) //卖出转移到现在
			{
				int val = dp[i-w-1][j] + j*po;
				while(l<r && B[r-1]<val) r--;
				A[r]=j; B[r++]=val;
				while(l<r && A[l]-mo>j) l++;
				dp[i][j] = max(dp[i][j], B[l]-j*po);
			}
		}
		printf("%d\n",dp[n][0]);
	}

    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值