HDU 3401 + CDOJ 880 Trade + 生日礼物

题意:

本金无限,告诉你每天买入卖出的价格和买入卖出的最多总数

让你求n天之后,最多能赚多少

很容易想到DP方程:

dp[i][j]表示第 i 天,有 j 股的最大收益

1.dp[i][j]=max(dp[ i-1 ][ j ],dp[ k ][ t ]-(j-t)*inval[i])

( k<i-w ,  j-t <= innum[ i ] )

且 dp[i][j]=dp[k][t]+(t-j)*outval[ i ]

(k<i - w, t - j <=outnum[ i ])

然后发现,这是个n^4的(n*p*n*p)

2.其实可以优化成n^3

因为dp[i-1][j]<=dp[i-1][j]

所以,k那一层的枚举可以省略

而改成 i-w-1

3.

最终优化:

对于买入:

dp[i][j]=max(dp[i-w-1][t]-(j-t)*inval[i])

展开括号得到:dp[i][j]=max(dp[i-w-1][t]+t*inval[i])-j*inval[i](因为提出来的式子与t无关)

对于卖出,同理

只是要注意,式子展开后方向是反的(三天三夜)

然后很容易发现,可以用单调队列搞

但是我是不会告诉你单调队列是怎么写的


AC代码如下

#include<bits/stdc++.h>
#define N 2001
using namespace std;
int t,n,p,w;
int innum[N],outnum[N],inval[N],outval[N];
int dp[N][N];
struct  data{
	int val,id;
};
deque<data>qin,qout;
void insert_out(data x)
{
	while(!qout.empty()&&qout.back().val<=x.val)qout.pop_back();
	qout.push_back(x);
}
void insert_in(data x)
{
	while(!qin.empty()&&qin.back().val<=x.val)qin.pop_back();
	qin.push_back(x);
}
void delate_in(int now,int x)
{
    while(!qin.empty()&&qin.front().id<x-innum[now])qin.pop_front();
}
void delate_out(int now,int x)
{
    while(!qout.empty()&&qout.front().id<x)qout.pop_front();
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d",&n,&p,&w);
		for(int i=1;i<=n;i++)
			scanf("%d%d%d%d",&inval[i],&outval[i],&innum[i],&outnum[i]);
		for(int i=0;i<=n;i++)
			for(int j=0;j<=p;j++)
				dp[i][j]=-1e9;
		for(int i=1;i<=w+1;i++)
		{
			for(int j=0;j<=innum[i];j++)
				dp[i][j]=-inval[i]*j;
			for(int j=0;j<=p;j++)
				dp[i][j]=max(dp[i][j],dp[i-1][j]);
		}
		data temp;
		for(int i=w+2;i<=n;i++)
		{
			qin.clear();
			qout.clear();
			int r=i-w-1;
			for(int t=0;t<=outnum[i];t++)
			{
				temp.id=t;
				temp.val=dp[r][t]+t*outval[i];
				insert_out(temp);
			}
			for(int j=0;j<=p;j++)
			{	
				dp[i][j]=max(dp[i][j],dp[i-1][j]);
				delate_in(i,j);
				if(j)dp[i][j]=max(dp[i][j],qin.front().val-j*inval[i]);
				delate_out(i,j);
				dp[i][j]=max(dp[i][j],qout.front().val-j*outval[i]);
				
				temp.val=dp[r][j]+j*inval[i];
				temp.id=j;
				insert_in(temp);
				if(j+outnum[i]+1<=p)
				{
					temp.id=j+outnum[i]+1;
					temp.val=dp[r][temp.id]+temp.id*outval[i];
					insert_out(temp);
				}
			}
		}
		int ans=0;
		for(int i=0;i<=p;i++)
			ans=max(ans,dp[n][i]);
		printf("%d\n",ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值