『单调队列优化DP』股票交易

P r o b l e m \mathrm{Problem} Problem

在这里插入图片描述

S o l u t i o n \mathrm{Solution} Solution

设f[i][j]表示前i天拥有j个股票的拥有的最多的前。

显然有如下几种情况:

  • 如果当前不进出股票
    f [ i ] [ j ] = max ⁡ ( f [ i ] [ j ] , f [ i − 1 ] [ j ] ) f[i][j]=\max (f[i][j],f[i-1][j]) f[i][j]=max(f[i][j],f[i1][j])
  • 所有股票都是今天买的
    f [ i ] [ j ] = max ⁡ ( f [ i ] [ j ] , j ∗ A p i ) f[i][j]=\max(f[i][j],j*Ap_i) f[i][j]=max(f[i][j],jApi)
  • 买股票
    f [ i ] [ j ] = max ⁡ j − A s i ≤ k &lt; j ( f [ i ] [ j ] , f [ i − w − 1 ] [ k ] − ( j − k ) ∗ A p i ) f[i][j]=\max_{j-As_i\leq k&lt;j}(f[i][j],f[i-w-1][k]-(j-k)*Ap_i) f[i][j]=jAsik<jmax(f[i][j],f[iw1][k](jk)Api)
  • 卖股票
    f [ i ] [ j ] = max ⁡ j &lt; k ≤ j + B s i ( f [ i ] [ j ] , f [ i − w − 1 ] [ k ] + ( k − j ) ∗ B p i ) f[i][j]=\max_{j&lt; k\leq j+Bs_i}(f[i][j],f[i-w-1][k]+(k-j)*Bp_i) f[i][j]=j<kj+Bsimax(f[i][j],f[iw1][k]+(kj)Bpi)

这显然是很简单了,我们再来考虑一下如何单调队列。

首先对于买股票的操作,单调队列一开始肯定要是空的,因为没有决策会小于0的。

对于卖股票的操作,由于需要逆序的操作,而且一开始的m一定在 j + B s i j+Bs_i j+Bsi的范围内,所有要有一个 m m m

C o d e \mathrm{Code} Code

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;
const int N = 2100;

int n, m, w;
int Ap[N], Bp[N], As[N], Bs[N], f[N][N], q[N];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' or c > '9') w |= c == '-', c = getchar();
	while (c >= '0' and c <= '9') s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

int main(void)
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	n = read(), m = read(), w = read();
	for (int i=1;i<=n;++i)
		Ap[i] = read(), Bp[i] = read(), As[i] = read(), Bs[i] = read();
		
	memset(f,-30,sizeof f); 
	f[0][0] = 0;
	for (int i=1;i<=n;++i)
	{
		for (int j=0;j<=As[i];++j) f[i][j] = -j * Ap[i]; 
		for (int j=0;j<=m;++j) f[i][j] = max(f[i][j],f[i-1][j]);
		if (i - w - 1 < 0) continue;
		#define calc1(e) (f[i-w-1][e]+e*Ap[i])
		#define calc2(e) (f[i-w-1][e]+e*Bp[i])
		int h = 1, t = 0;
		for (int j=0;j<=m;++j)
		{
			while (h <= t and q[h] < j-As[i]) h ++;
			if (h <= t) f[i][j] = max(f[i][j],calc1(q[h]) - j * Ap[i]);
			while (h <= t and calc1(j) >= calc1(q[t])) t --;
			q[++t] = j;
		}
		h = 1, t = 1;q[1] = m;
		for (int j=m;j>=0;--j)
		{
			while (h <= t and q[h] > j+Bs[i]) h ++;
			if (h <= t) f[i][j] = max(f[i][j],calc2(q[h]) - j * Bp[i]);
			while (h <= t and calc2(j) >= calc2(q[t])) t --;
			q[++t] = j;
		}
	}
	
	printf("%d\n", f[n][0]);
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值