股票交易(BZOJ-1855)(Dp转移优化+单调队列优化Dp)

2 篇文章 0 订阅

前言

Dp优化最难理解了…

题目

传送门
BZOJ
Vjudge

思路

ap买入价 bp卖出价 as买入最大股 bs卖出最大股

首先,我们要写出一般形式的Dp。
我们定义:
f [ i ] [ j ] : 第 i 天 此 时 手 上 股 票 数 为 j , 前 i 天 最 大 获 利 f[i][j]:第i天此时手上股票数为j,前i天最大获利 f[i][j]:ij,i
①我们先初始化边界,:
f [ i ] [ j ] = { 0  i==0&&j==0 − I N F otherwise f[i][j]=\begin{cases} 0&\text{ i==0\&\&j==0}\\ -INF&\text{otherwise} \end{cases} f[i][j]={0INF i==0&&j==0otherwise

②然后我们如果写出 O ( n 4 ) O(n^4) O(n4)是极为不优秀的,于是预处理Dp转移还要继续, 考虑只买一次 j j j 张股票的最小花费,如果可以买,分为买和不买,取max:
f [ i ] [ j ] = { m a x { − a p [ i ] ∗ j , f [ i − 1 ] [ j ] } j∈[0,as[i]] − a p [ i ] ∗ j j∈(as[i],Maxp] f[i][j]=\begin{cases} max\{-ap[i]*j,f[i-1][j]\}&\text{j∈[0,as[i]]}\\ -ap[i]*j&\text{j∈(as[i],Maxp]} \end{cases} f[i][j]={max{ap[i]j,f[i1][j]}ap[i]jj∈[0,as[i]]j∈(as[i],Maxp]
这样我们就可以接下来就少一维枚举i的转移点了。

然后我们考虑转移,我们买和卖分开转移:
①买,考虑从(i-w-1)天转移(由于优化初始值[0,i-w-1)不考虑)
f [ i ] [ j ] = m a x { f [ i − w − 1 ] [ k ] − a p [ i ] ∗ ( j − k ) } f[i][j]=max\{f[i-w-1][k]-ap[i]*(j-k)\} f[i][j]=max{f[iw1][k]ap[i](jk)}
( 0 &lt; = k &lt; j &lt; = m i n ( k + a s [ i ] , M a x p ) ) (0&lt;=k&lt;j&lt;=min(k+as[i],Maxp)) (0<=k<j<=min(k+as[i],Maxp))
②卖,同上
f [ i ] [ j ] = m a x { f [ i − w − 1 ] [ k ] + b p [ i ] ∗ ( k − j ) } f[i][j]=max\{f[i-w-1][k]+bp[i]*(k-j)\} f[i][j]=max{f[iw1][k]+bp[i](kj)}
( M a x p &gt; = k &gt; j &gt; = m a x ( k − b s [ i ] , 0 ) ) (Maxp&gt;=k&gt;j&gt;=max(k-bs[i],0)) (Maxp>=k>j>=max(kbs[i],0))

对于答案我们只需要让ans= m a x { f [ i ] [ 0 ] } max\{f[i][0]\} max{f[i][0]}即可,因为如果剩下股票之前不买显然更优,但是,这是一个 O ( n 3 ) O(n^3) O(n3)的转移,显然是会TLE的

于是我们考虑优化
我们记 p = i − w − 1 p=i-w-1 p=iw1
就拿买来说吧,
我们对状态转移方程进行变形:
f [ i ] [ j ] = m a x { f [ i − w − 1 ] [ k ] + a p [ i ] ∗ k − a p [ i ] ∗ j } f[i][j]=max\{f[i-w-1][k]+ap[i]*k-ap[i]*j\} f[i][j]=max{f[iw1][k]+ap[i]kap[i]j}
我们记 g [ k ] = f [ i − w − 1 ] [ k ] + a p [ i ] ∗ k g[k]=f[i−w−1][k]+ap[i]∗k g[k]=f[iw1][k]+ap[i]k

我们可以发现,对于i固定时,对于 g [ k ] g[k] g[k]是可以用双端队列维护它的单调性的,队列中存 k 和 g [ k ] k和g[k] kg[k] 记为cnt和val,队列的 v a l val val是单调递减的

对于队首元素,如果 c n t + a s [ i ] &lt; j cnt+as[i]&lt;j cnt+as[i]<j即j已经不在 [ c n t , c n t + a s [ i ] ] [cnt,cnt+as[i]] [cnt,cnt+as[i]]范围内了,pop掉

再考虑即将入队的 j 和 g [ j ] j和g[j] jg[j],如果 g [ j ] g[j] g[j]比现在队尾更优,显然队尾直接pop掉,因为队尾比它将来更早出队
然后
f [ i ] [ j ] = m a x { f [ i ] [ j ] , Q . f r o n t ( ) . v a l − a p [ i ] ∗ j } f[i][j]=max\{f[i][j],Q.front().val-ap[i]*j\} f[i][j]=max{f[i][j],Q.front().valap[i]j}即可

对于卖倒过来做即可,需注意入队出队条件和范围.

代码

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return f*x;
}
#define MAXN 2000
#define INF 0x3f3f3f3f
struct node{
	int cnt,val;
	node(){}
	node(int C,int V){cnt=C,val=V;}
};
int f[MAXN+5][MAXN+5];
deque<node> Q;
int main(){//f[i][j]:第i天此时手上股票数为j的前i天最大获利
	int ans=-INF;
	memset(f,-0x3f,sizeof(f)),f[0][0]=0;
	int T=read(),MaxP=read(),w=read();
	for(int i=1;i<=T;i++){
		int ap=read(),bp=read(),as=read(),bs=read();//买入价,卖出价,买入最大股,卖出最大股
		for(int j=0;j<=as;j++) f[i][j]=max(f[i-1][j],-ap*j);
        for(int j=as+1;j<=MaxP;j++) f[i][j]=f[i-1][j];
		int p=i-w-1;
		if(p<0) continue;
		Q.clear();
		for(int j=0;j<=MaxP;j++){
			while(!Q.empty()&&Q.front().cnt+as<j) Q.pop_front();
			while(!Q.empty()&&Q.back().val<=f[p][j]+ap*j) Q.pop_back();
			Q.push_back(node(j,f[p][j]+ap*j));
			f[i][j]=max(f[i][j],Q.front().val-ap*j);
		}
		Q.clear();
		for(int j=MaxP;j>=0;j--){
			while(!Q.empty()&&j+bs<Q.front().cnt) Q.pop_front();
			while(!Q.empty()&&Q.back().val<=f[p][j]+bp*j) Q.pop_back();
			Q.push_back(node(j,f[p][j]+bp*j));
			f[i][j]=max(f[i][j],Q.front().val-bp*j);
		}
		ans=max(ans,f[i][0]);
	}
	printf("%d\n",ans);
	return 0;
}

题外话

一道好题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值