【SCOI2010】【单调队列优化DP】股票交易

15 篇文章 0 订阅

很明显的DP,不过省选主要考察的就是DP优化

朴素的做法如下:
状态:用f[i][j]表示前i天中,最后1天收盘时手中还持有j股的股票所能得到的最大收益。

转移方程:f[i][j] = max{f[i - 1][j],f[i - W - 1][j - k1] - ap[i] * k1,f[i - W - 1][j + k2] + bp[i] * k2}。
其中,k1 <= as[i], k2 <= bs[i]。

以上算法的复杂度是O(T * maxP ^ 2),显然过不了极限数据。

对于以上转移方程方程,可做如下转化:
令t = i - W - 1,p = j - k1, q = j + k2。
忽略f[i - 1][j]项,则有:
f[i][j] = max{f[t][p] - ap[i] * (j - p),f[t][q] + bp[i] * (q - j)},
且p >= j - as[i], q <= j + bs[i]。
即f[i][j] = max{f[t][p] + ap[i] * p - ap[i] * j,    (1)
                f[t][q] + bp[i] * q - bp[i] * j}。  (2)
这样一来,可以发现(1)式中,f[t][p] + ap[i] * p部分,和(2)式中f[t][q] + bq[i] * q部分只与p, q有关,于是可以使用单调队列优化。(以上转自Whjpji)

代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 2000 + 10;
struct day
{
	int ap,bp,as,bs;
}deal[maxn];
int f[maxn][maxn];
int q[maxn],val[maxn];
int t,maxp,w;
int l,r;
int ans = 0;
void init()
{
	freopen("bzoj1855.in","r",stdin);
	freopen("bzoj1855.out","w",stdout);
}

int max(int a,int b)
{
	return a > b ? a : b;
}

void readdata()
{
	scanf("%d%d%d",&t,&maxp,&w);
	for(int i = 1;i <= t;i++)
	{
		scanf("%d%d%d%d",&deal[i].ap,&deal[i].bp,&deal[i].as,&deal[i].bs);
	}
}

void solve()
{
	memset(f,~0x3f,sizeof(f));
	f[0][0] = 0;
	for(int i = 1;i <= t;i++)
	{
		for(int j = 0;j <= deal[i].as;j++)f[i][j] = -deal[i].ap * j;
		for(int j = 0;j <= maxp;j++)
			f[i][j] = max(f[i][j],f[i-1][j]);
		int t = i - w - 1;
		if(t >= 0)
		{
			l = r = 0;
			for(int j = 0;j <= maxp;j++)
			{
				while(l < r && q[l] < j - deal[i].as)++l;
				while(l < r && f[t][j] + j * deal[i].ap >= val[r-1])--r;
				val[r] = f[t][j] + j * deal[i].ap;
				q[r++] = j;
				if(l < r)f[i][j] = max(f[i][j],val[l] - j * deal[i].ap);
			}
			l = r = 0;
			for(int j = maxp;j >= 0;j--)
			{
				while(l < r && q[l] > j + deal[i].bs)++l;
				while(l < r && f[t][j] + j * deal[i].bp >= val[r-1])--r;
				val[r] = f[t][j] + j * deal[i].bp;
				q[r++] = j;
				if(l < r)f[i][j] = max(f[i][j],val[l] - j * deal[i].bp);
			}
		}
		ans = max(f[i][0],ans);
	}
	printf("%d",ans);
}

int main()
{
	init();
	readdata();
	solve();
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值