单调队列优化的DP HDU3401 Trade

19 篇文章 0 订阅
17 篇文章 0 订阅

单调队列优化的DP

单调队列整理链接

动态规划,我们经常会遇到转移的过程需要和前面 ( L , L + m ) (L,L+m) (L,L+m)的区间转移的操作

  • 若每次只需要 ( L , L + m ) (L,L+m) (L,L+m)的最大或最小值
  • m m m的大小固定
    那么我们就可以用单调队列来取 ( L , L + m ) (L,L+m) (L,L+m)的最值来优化动态规划

HDU3401 Trade

题意

X某预知了接下来 T T T天的某只股票情况
i i i天,买进该股票价格为 A P i AP_{i} APi,卖出该股票价格为 B P i BP_{i} BPi,可以买进 S A i SA_{i} SAi股,可以卖出 S B i SB_{i} SBi
手上最多持有 M a x p Maxp Maxp股,每次交易(买进、卖出均算一次交易)后,w天不能交易(比如第1天买入股票,要在w+2天才能卖出股票)
本钱无限,问最大收益

分析

状态

相信大家都会想到 d p [ T ] [ m a x p ] dp[T][maxp] dp[T][maxp],第一下标表示第几天,第二下标表示持有股票数

状态转移

i i i为当前天数, j j j为当前持有股票数, r r r w w w天前的天数 ( r &lt; j − w − 1 ) (r&lt;j-w-1) (r<jw1) k k k r r r天时手持股票数

  • 不买入股票: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j ] ) dp[i][j]=max(dp[i-1][j],dp[i][j]) dp[i][j]=max(dp[i1][j],dp[i][j])
  • i i i天买入 j − k j-k jk股票: d p [ i ] [ j ] = m a x ( d p [ r ] [ k ] − ( j − k ) ∗ a p [ i ] , d p [ i ] [ j ] ) dp[i][j]=max(dp[r][k]-(j-k)*ap[i],dp[i][j]) dp[i][j]=max(dp[r][k](jk)ap[i],dp[i][j])
  • i i i天卖出 j − k j-k jk股票: d p [ i ] [ j ] = m a x ( d p [ r ] [ k ] + ( j − k ) ∗ b p [ i ] , d p [ i ] [ j ] ) dp[i][j]=max(dp[r][k]+(j-k)*bp[i],dp[i][j]) dp[i][j]=max(dp[r][k]+(jk)bp[i],dp[i][j])
    显然后面两个公式复杂度很高, r r r k k k都不确定

由第一个公式可得, d p [ r ] [ k ] &gt; d p [ r − 1 ] [ k ] dp[r][k]&gt;dp[r-1][k] dp[r][k]>dp[r1][k],所以 r r r i − w − 1 i-w-1 iw1即可
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j ] ) dp[i][j]=max(dp[i-1][j],dp[i][j]) dp[i][j]=max(dp[i1][j],dp[i][j])
d p [ i ] [ j ] = m a x ( d p [ i − w − 1 ] [ k ] − ( j − k ) ∗ a p [ i ] , d p [ i ] [ j ] ) dp[i][j]=max(dp[i-w-1][k]-(j-k)*ap[i],dp[i][j]) dp[i][j]=max(dp[iw1][k](jk)ap[i],dp[i][j])
d p [ i ] [ j ] = m a x ( d p [ i − w − 1 ] [ k ] + ( j − k ) ∗ b p [ i ] , d p [ i ] [ j ] ) dp[i][j]=max(dp[i-w-1][k]+(j-k)*bp[i],dp[i][j]) dp[i][j]=max(dp[iw1][k]+(jk)bp[i],dp[i][j])
但k的值复杂度依旧很高

我们将两个式子变换以下
d p [ i − w − 1 ] [ k ] − ( j − k ) ∗ a p [ i ] = d p [ i − w − 1 ] [ k ] + k ∗ a p [ i ] − j ∗ a p [ i ] dp[i-w-1][k]-(j-k)*ap[i]=dp[i-w-1][k]+k*ap[i]-j*ap[i] dp[iw1][k](jk)ap[i]=dp[iw1][k]+kap[i]jap[i]
d p [ i − w − 1 ] [ k ] + ( j − k ) ∗ b p [ i ] = d p [ i − w − 1 ] [ k ] + k ∗ b p [ i ] − j ∗ b p [ i ] dp[i-w-1][k]+(j-k)*bp[i]=dp[i-w-1][k]+k*bp[i]-j*bp[i] dp[iw1][k]+(jk)bp[i]=dp[iw1][k]+kbp[i]jbp[i]
我们发现 j ∗ a p [ i ] j*ap[i] jap[i]是固定的,只需要求出 d p [ i − w − 1 ] [ k ] + k ∗ a p [ i ] ( j − a s [ i ] − 1 &lt; k &lt; j + 1 ) dp[i-w-1][k]+k*ap[i](j-as[i]-1&lt;k &lt; j+1) dp[iw1][k]+kap[i](jas[i]1<k<j+1)的最大值即可
同理求出 d p [ i − w − 1 ] [ k ] + k ∗ b p [ i ] ( j − 1 &lt; k &lt; j + b s [ i ] + 1 ) dp[i-w-1][k]+k*bp[i](j-1&lt;k&lt;j+bs[i]+1) dp[iw1][k]+kbp[i](j1<k<j+bs[i]+1)的最大值
而最大值维护就可以用单调队列维护了

代码详解

预处理 [ 1 , w + 1 ] [1,w+1] [1,w+1]
void Pre_Work() {
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= maxp; j++)
			dp[i][j] = -inf;
	for (int i = 1; i <= w + 1; i++) {
		for (int j = 0; j <= as[i]; j++)
			dp[i][j] = max(dp[i - 1][j], -ap[i] * j);
		for (int j = as[i] + 1; j <= maxp; j++)
			dp[i][j] = max(dp[i][j], dp[i - 1][j]);
	}
}
d p [ i − w − 1 ] [ k ] + k ∗ a p [ i ] ( j − a s [ i ] − 1 &lt; k &lt; j + 1 ) dp[i-w-1][k]+k*ap[i](j-as[i]-1&lt;k &lt; j+1) dp[iw1][k]+kap[i](jas[i]1<k<j+1)的最大值
void Queue_bug(int j) {
	int l = 1, r = 0, i = j - w - 1, now;
	for (int k = 0; k <= maxp; k++) {
		now = k * ap[j] + dp[i][k];
		while (r >= l && stack[r].first <= now)r--;
		stack[++r] = pii(now, k);
		while (r >= l && stack[l].second < k - as[j])l++;
		ans[k] = stack[l].first;
	}
}
d p [ i − w − 1 ] [ k ] + k ∗ b p [ i ] ( j − 1 &lt; k &lt; j + b s [ i ] + 1 ) dp[i-w-1][k]+k*bp[i](j-1&lt;k&lt;j+bs[i]+1) dp[iw1][k]+kbp[i](j1<k<j+bs[i]+1)的最大值
void Queue_sell(int j) {
	int l = 1, r = 0, i = j - w - 1, now;
	for (int k = maxp; k >= 0; k--) {
		now = k * bp[j] + dp[i][k];
		while (r >= l && stack[r].first <= now)r--;
		stack[++r] = pii(now, k);
		while (r >= l && stack[l].second > k + bs[j])l++;
		ans[k] = stack[l].first;
	}
}

AC代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <iomanip>
using namespace std;
#pragma warning (disable:4996)
typedef pair<int, int> pii;
const int maxn = 2005;
const int inf = 1e9;;
int n, maxp, w;
int ap[maxn], bp[maxn], as[maxn], bs[maxn];
void Read() {
	scanf("%d%d%d", &n, &maxp, &w);
	for (int i = 1; i <= n; i++)
		scanf("%d%d%d%d", &ap[i], &bp[i], &as[i], &bs[i]);
}
int dp[maxn][maxn];
void Pre_Work() {
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= maxp; j++)
			dp[i][j] = -inf;
	for (int i = 1; i <= w + 1; i++) {
		for (int j = 0; j <= as[i]; j++)
			dp[i][j] = max(dp[i - 1][j], -ap[i] * j);
		for (int j = as[i] + 1; j <= maxp; j++)
			dp[i][j] = max(dp[i][j], dp[i - 1][j]);
	}
}
pii stack[maxn];
int ans[maxn];
void Queue_bug(int j) {
	int l = 1, r = 0, i = j - w - 1, now;
	for (int k = 0; k <= maxp; k++) {
		now = k * ap[j] + dp[i][k];
		while (r >= l && stack[r].first <= now)r--;
		stack[++r] = pii(now, k);
		while (r >= l && stack[l].second < k - as[j])l++;
		ans[k] = stack[l].first;
	}
}
void Queue_sell(int j) {
	int l = 1, r = 0, i = j - w - 1, now;
	for (int k = maxp; k >= 0; k--) {
		now = k * bp[j] + dp[i][k];
		while (r >= l && stack[r].first <= now)r--;
		stack[++r] = pii(now, k);
		while (r >= l && stack[l].second > k + bs[j])l++;
		ans[k] = stack[l].first;
	}
}
void Dp() {
	for (int i = w + 2; i <= n; i++) {
		Queue_bug(i);
		for (int j = 0; j <= maxp; j++)
			dp[i][j] = max(dp[i - 1][j], ans[j] - j * ap[i]);
		Queue_sell(i);
		for (int j = 0; j <= maxp; j++)
			dp[i][j] = max(dp[i][j], ans[j] - j * bp[i]);
	}
}
int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		Read();
		Pre_Work();
		Dp();
		int Max = 0;
		for (int i = 0; i <= maxp; i++)
			Max = max(Max, dp[n][i]);
		printf("%d\n", Max);
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值