题解 P2569 【[SCOI2010]股票交易】

5 篇文章 0 订阅
3 篇文章 0 订阅

题解- P2569 股票交易

  • 题目意思

    由于题面过长,不再描述。
    戳这里

  • S o l Sol Sol

    这道题目想清楚后还是不难的,是一道不错的单调队列练习题。

    首先我们要明确状态

    f i , j f_{i,j} fi,j表式到第 i i i天拥有 j j j个股票的最大收益。

    这个状态还是挺显然的,不像某些题目卡状态。

    对于转移,要分成多种情况来考虑:

    ( I ) (I) (I) 我们就是没有任何积蓄的情况下买(相当于一个初始化),此时转移显然

    $f_{i,j}=-j \times AP_i $ ( j ≤ M a x P ) (j\leq MaxP) (jMaxP)

    ( I I ) (II) (II) 我们当天选择不买也不买

    f i , j = max ⁡ [ f i − 1 , j , f i , j ] f_{i,j}=\max [f_{i-1,j},f_{i,j}] fi,j=max[fi1,j,fi,j]

    这个应该比较好理解就是继承原来的最大收益。

    ( I I I ) (III) (III) 已经有资金基础下买股票,此时如何转移呢?

    我们考虑上次买有多少股票 k k k,则转移很显然:

    f i , j = max ⁡ [ f i − w − 1 , k − ( j − k ) × A P i , f i , j ] f_{i,j}=\max [f_{i-w-1,k}-(j-k) \times AP_i,f_{i,j}] fi,j=max[fiw1,k(jk)×APi,fi,j]

    如何理解呢?就是上次购买时间为 ( i − w − 1 ) (i-w-1) (iw1),间隔 w w w天买嘛。且因为这次是买进,数量要比上次多,的所以 k ≤ j k\leq j kj。所以这次转移是这样的。

    ( I V ) (IV) (IV) 已经有资金基础下卖股票,此时如何转移呢?

    我们考虑上次买有多少股票 k k k,则转移很显然:

    f i , j = max ⁡ [ f i − w − 1 , k + ( k − j ) × B P i , f i , j ] f_{i,j}=\max [f_{i-w-1,k}+(k-j) \times BP_i,f_{i,j}] fi,j=max[fiw1,k+(kj)×BPi,fi,j]

    如何理解呢(和 I I I III III同理)?就是上次购买时间为 ( i − w − 1 ) (i-w-1) (iw1),间隔 w w w天买嘛。且因为这次是卖出,数量要比上次少,的所以 j ≤ k j\leq k jk。所以这次转移是这样的。

    但是这样的时间复杂度是存在问题的,大概为 O ( T × M a x P 2 ) O(T \times MaxP^2) O(T×MaxP2),可以获得 50 50 50分的好成绩。我们考虑优化,单调队列?线段树?

    此时我们可以发现转移符合单调性优化原则,所以可以用单调队列来优化。对于 ( I I I ) , ( I V ) (III),(IV) (III),(IV)情况我们就像滑动窗口一样做。只要做 ( I I I ) (III) (III)的时候正着扫, ( I V ) (IV) (IV)时候到这做(因为卖出去,上次股票肯定比这次多)。应该比较好理解吧。

    这里还有一个显而易见的优化:就是如果现在的天数 ≤ w \leq w w是不用做 ( I I I ) , ( I V ) (III),(IV) (III),(IV)的。

    这样复杂度就变为 O ( T × M a x P ) O(T \times MaxP) O(T×MaxP),可以轻松过掉这道题目。

  • C o d e Code Code

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

inline int read()
{
	int sum=0,ff=1; char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') ff=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		sum=sum*10+(ch^48),ch=getchar();
	return sum*ff;
}

const int N=2005;

int T,MP,W,AP[N],BP[N],AS[N],BS[N],ans;
int f[N][N],q[N];

inline int max(int i,int j)
{
	return (i>j)?i:j;
}

signed main()
{
	T=read();
	MP=read();
	W=read();
	for ( int i=1;i<=T;i++ )
	{
		AP[i]=read();
		BP[i]=read();
		AS[i]=read();
		BS[i]=read();
	}
	memset(f,-63,sizeof(f));
	for ( int i=1;i<=T;i++ ) 
	{
		for ( int j=0;j<=AS[i];j++ ) 
			f[i][j]=-1ll*j*AP[i];
		for ( int j=0;j<=MP;j++ ) 
			f[i][j]=max(f[i][j],f[i-1][j]);
		if(i<=W) continue;
		int head=1,tail=0;
		for ( int j=0;j<=MP;j++ )
		{
			while(head<=tail && q[head]<j-AS[i]) head++;
			while(head<=tail && f[i-W-1][q[tail]]+q[tail]*AP[i]<=f[i-W-1][j]+j*AP[i]) tail--;
			q[++tail]=j;
			if(head<=tail) 
				f[i][j]=max(f[i-W-1][q[head]]-(j-q[head])*AP[i],f[i][j]);
		}
		head=1,tail=0;
		for ( int j=MP;j>=0;j-- )
		{
			while(head<=tail && q[head]>j+BS[i]) head++;
			while(head<=tail && f[i-W-1][q[tail]]+q[tail]*BP[i]<=f[i-W-1][j]+j*BP[i]) tail--;
			q[++tail]=j;
			if(head<=tail) 
				f[i][j]=max(f[i-W-1][q[head]]+(q[head]-j)*BP[i],f[i][j]);
		}
	}
	ans=-1e12;
	for ( int i=0;i<=MP;i++ ) ans=max(ans,f[T][i]);
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值