首页 > 代码库 > HDU - 3401 Trade

HDU - 3401 Trade

题意:让你炒股票,每天都有买进的额度和价格以及卖出的额度和价格,并规定时间和最多的持有股票是多少,而且买卖操作要隔w+1天求最高的利润

思路:显然分三种情况:不买不卖,买,卖,设dp[i][j]表示第i天持有j股票的最高利润

如果不买不卖的话就是:dp[i][j]=dp[i-1][j]

                               买: dp[i][j]=max(dp[i][j],dp[pre][k]-(j-k)*ap[i])

                               卖: dp[i][i]=max(dp[i][j],dp[pre][k]+(k-j)*bp[i])

拿买来说的话:dp[pre][k]-(j-k)*ap[i]=dp[pre][k]-j*ap[i]+k*ap[i],我们的目的是尽量大,所以我们尽量找一个最大的利润,那么维持一个单调队列就是了,重点是队尾的操作,如果后一个比前一个利润高的话,就移动,这样就可以后面的操作数是最优的

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int MAXN = 2010;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int Time,maxp,w;
int ap[MAXN],bp[MAXN],as[MAXN],bs[MAXN];
int dp[MAXN][MAXN];
pii que[MAXN];
int st,ed;

int main(){
	int t;
	scanf("%d", &t);
	while (t--){
		memset(dp[0],-INF,sizeof(dp[0]));
		dp[0][0] = 0;
		scanf("%d%d%d", &Time, &maxp, &w);
		for (int i = 1; i <= Time; i++)
			scanf("%d%d%d%d", &ap[i], &bp[i], &as[i], &bs[i]);
		int ans = 0;
		for (int i = 1; i <= Time; i++){
			memcpy(dp[i], dp[i-1], sizeof(dp[i]));
			if (i <= w+1){
				for (int j = 0; j <= as[i]; j++)
					dp[i][j] = max(dp[i-1][j], -ap[i]*j);
				continue;
			}
			st = ed = 0;
			int pre = i-w-1;
			for (int j = 0; j <= maxp; j++){
				pii elem = pii(dp[pre][j]+j*ap[i],j);
				que[ed++] = elem;
				while (ed-st >= 1 && que[st].second < j-as[i])
					st++;
				while (ed-st >= 2 && que[ed-1].first >= que[ed-2].first)
					que[ed-2] = que[ed-1],ed--;
				dp[i][j] = max(dp[i][j], que[st].first-j*ap[i]);
				ans = max(ans, dp[i][j]);
			}
			st = ed = 0;
			for (int j = maxp; j >= 0; j--){
				pii elem = pii(dp[pre][j]+j*bp[i], j);
				que[ed++] = elem;
				while (ed-st >= 1 && que[st].second > j+bs[i])
					st++;
				while (ed-st >= 2 && que[ed-1].first >= que[ed-2].first)
					que[ed-2] = que[ed-1],ed--;
				dp[i][j] = max(dp[i][j], que[st].first-j*bp[i]);
				ans = max(ans, dp[i][j]);
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}