P2569 [SCOI2010] 股票交易题解

股票交易题解

大致思路:

这道题一眼动态规划,我们来设一个状态,设 d p i , j dp_{i,j} dpi,j 为前 i i i 天拥有股票数量 j j j 的最大收益,而状态转移方程需要分类讨论。

  • 一直单纯买,那么转移方程很明显,一开始我们会将所有数据初始为极小的数,为 d p i , j = j × a p i dp_{i,j} = j × ap_i dpi,j=j×api

  • 不买也不卖,那么状态转移方程由上面方法转移而来,非常明显,与前一天中找收益大的,转移方程为 d p i , j = max ⁡ ( d p i , j , d p i − 1 , j ) dp_{i,j} = \max(dp_{i,j},dp_{i - 1,j}) dpi,j=max(dpi,j,dpi1,j)

  • 在上面两种方法的情况下买股票,这个转移方程稍微复杂一些,由于每次买卖之间需要相隔 w w w 天,也就是上一次进行是在第 i − w − 1 i - w - 1 iw1 天,我们假设第 i − w − 1 i - w - 1 iw1 天拥有 k k k 张股票,而 k k k 一定比 j j j 小,因为股票买的要尽可能多,所以,但又不能太多,限制最多 a s as as 张,所以底线是 j − a s j - as jas,而本次共买了 j − k j - k jk 张股票,要用去 ( j − k ) × a p i (j - k) × ap_i (jk)×api 元,那么转移方程就是 d p i , j = max ⁡ ( 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}) dpi,j=max(dpiw1,k(jk)×api,dpi,j)

  • 在最上面两种情况下卖股票,道理与上一样,就不细讲了,得出转移方程 d p i , j = max ⁡ ( 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}) dpi,j=max(dpiw1,k+(jk)×bpi,dpi,j)

而以上转移方程,需要在 O ( n 3 ) O(n ^ 3) O(n3) 的时间复杂度下完成,一看数据 200 0 3 2000 ^ 3 20003 肯定过不了。

那么我们需要优化,在下面两种情况中优化,因为那些方程符合单调性优化,以第三种情况为准,用分配律 d p i , j = max ⁡ ( d p i − w − 1 , k + k × a p i , d p i , j ) − j × a p i dp_{i,j} = \max(dp_{i - w - 1,k} + k × ap_i,dp_{i,j}) - j × ap_i dpi,j=max(dpiw1,k+k×api,dpi,j)j×api。第四种情况也是如此,再用单调队列优化,现在时间复杂度降为了 O ( n ) O(n) O(n),可以通过。

代码实现:

#include <bits/stdc++.h>
#define int long long
#define fo(i,x,y) for(int i = x;i <= y;i ++)
#define fr(i,x,y) for(int i = x;i >= y;i --)
#define in(p) cin >> p
#define out(p) cout << p
#define k(n) puts("qwq")
using namespace std;
const int N = 2005;
const int MOD = 123456789;
int mx = 0;
int mn = 0x3f3f3f;
int n, maxp, w, dp[N][N], ap, bp, as, bs, res;
deque <int> q;
inline int read(){
	int r = 0,w = 1;
	char c = getchar();
	while (c < '0' || c > '9'){
		if (c == '-'){
			w = -1;
		}
		c = getchar();
	}
	while (c >= '0' && c <= '9'){
		r = (r << 3) + (r << 1) + (c ^ 48);
		c = getchar();
	}
	return r * w;
}
void write(int x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
	return;
}
bool cmp(int l, int r)
{
	return l > r;
}

signed main()
{
	n = read();
	maxp = read();
	w = read();
	memset(dp, -0x3f, sizeof dp);
	q.clear();
	for(int i = 1;i <= n;i ++)
	{
		int l = 1, r = 0; 
		ap = read();
		bp = read();
		as = read();
		bs = read();
		for(int j = 0;j <= as; j ++)//方案1 
		{
			dp[i][j] = (-ap) * j;
		}
		for(int j = 0;j <= maxp;j ++)//方案2 
		{
			dp[i][j] = max(dp[i][j], dp[i - 1][j]);
		}
		if(i <= w) continue;//防越界
		
		for(int j = 0;j <= maxp;j ++) //方案3 
		{
			while(q.size() && q.front() < j - as)	q.pop_front();
			while(q.size() && dp[i - w - 1][q.back()] + q.back() * ap <= dp[i - w - 1][j] + j * ap) q.pop_back();
			q.push_back(j);
			if(q.size()) dp[i][j] = max(dp[i][j], dp[i - w - 1][q.front()] + ap * q.front() - j * ap);
		}
		q.clear();
		l = 1, r = 0;
		for(int j = maxp;j >= 0;j --)//方案4 
		{
			while(q.size() && q.front() > j + bs)	q.pop_front();
			while(q.size() && dp[i - w - 1][q.back()] + q.back() * bp <= dp[i - w - 1][j] + j * bp) q.pop_back();
			q.push_back(j);
			if(q.size()) dp[i][j] = max(dp[i][j], dp[i - w - 1][q.front()] + bp * q.front() - j * bp);
		}
		q.clear();
	}
	for(int i = 0;i <= maxp;i ++)
	{
		res = max(res, dp[n][i]);
		//cout << dp[n][i] << "\n";
	}
	write(res);
	return 0;
	k(1);
}
  • 31
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值