Loading

P2569 股票交易题解

P2569 股票交易 题解

思路

容易想到 \(dp\),我们设 \(f_{i,j}\) 表示第 \(i\) 天剩下 \(j\) 张股票最多可以赚到多少钱

显然由于 \(ap\ge bp\) 所以一天之内不可能既买又卖,只能只买/只卖/不卖不卖

不卖不卖的转移十分容易:

\[f_{i,j}=f_{i-1,j} \]

而只买分为两种情况:凭空买,在前面交易的基础上买

凭空买的话,转移如下:

\[f_{i,j}=-ap_i\times j \]

在前面交易的基础上买的转移如下:

\[f_{i,j}=\max\{f_{i-w-1,k}-(j-k)\times ap_i\} \]

卖的话只能在前面交易的基础上卖,转移如下:

\[f_{i,j}=\max\{f_{i-w-1,k}+(k-j)\times bp_i\} \]

显然,前两个转移操作是 \(\text O(nm)\) 的,可以接受

而后两个转移则是 \(\text O(nm^2)\) 需要优化

对于转移 \(3\) 我们可以正序枚举 \(k\),然后搞一个单调队列维护其单调性,以此来转移

因为

\[f_{i,j}=\max\{f_{i-w-1,k}-(j-k)\times ap_i\}=(f_{i-w-1,k}+k\times ap_i)-j\times ap_i \]

显然 \(j\times ap_i\) 是一个常数,而前半部分的 \(f_{i-w-1,k}+k\times ap_i\) 只跟 \(k\) 的取值有关,因此我们可以用单调队列来维护这个东西

这样复杂度就由 \(\text O(nm^2)\to \text O(nm)\)

可以接受了

code

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int N=2e3+5;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int f[N][N],q[N];

signed main(){
	int n=read(),m=read(),w=read();
	memset(f,0xcf,sizeof(f));//初始赚的钱为无穷小
	for(int i=1;i<=n;++i){
		int ap=read(),bp=read(),as=read(),bs=read();
		for(int j=0;j<=as;++j) f[i][j]=-1*j*ap;
		for(int j=0;j<=m;++j) f[i][j]=max(f[i][j],f[i-1][j]);
		if(i<=w) continue;
		int l=1,r=0;
		for(int j=0;j<=m;++j){
			while(l<=r&&q[l]<j-as) ++l;
			while(l<=r&&f[i-w-1][q[r]]+q[r]*ap<=f[i-w-1][j]+j*ap) --r;
			q[++r]=j;
			if(l<=r)
				f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*ap-j*ap);
		}
		l=1,r=0;
		for(int j=m;j>=0;--j){
			while(l<=r&&q[l]>j+bs) ++l;
			while(l<=r&&f[i-w-1][q[r]]+q[r]*bp<=f[i-w-1][j]+j*bp) --r;
			q[++r]=j;
			if(l<=r)
				f[i][j]=max(f[i][j],f[i-w-1][q[l]]+q[l]*bp-j*bp);
		}
	}
	printf("%d",f[n][0]);//全卖掉最小
}
posted @ 2022-07-29 11:23  Into_qwq  阅读(22)  评论(0编辑  收藏  举报