SCOI2010 股票交易
这道题也是很好的单调队列优化DP,不过细节很多。
感觉这个题的状态其实可以借鉴上一道题fence?我们能想到用dp[i][j]表示这位大佬在第i天持有j份股票得到的最大的钱数,我们进行分类讨论。
首先是不进行交易,这个很显然是dp[i][j] = max(dp[i][j],dp[i-1][j]),这个其实很重要,因为它把不交易的天的情况都转移过来了,这样的话,我们在考虑进行交易的时候,只要考虑第i-w-1天的情况即可,之前的情况无需考虑。
然后是买,那么就有dp[i][j] = max{dp[i-w-1][k] - ap[i] * (j - k)},至于卖,就有dp[i][j] = max{dp[i-w-1][k] + bp[i]*(k-j)};
其实这两个式子是一样的,只不过常数不同(和i有关)
我们朴素的做法还是三重循环,把式子拆开之后发现仍然转化成内层决策的值与外层状态无关的情况,直接用单调队列分别维护即可。
不过细节有点多,首先是这个边界比较难判断,再者,比较方便的写法是对于买的情况正序枚举,卖的情况倒序枚举,其实正着枚举应该是对的,不过很难写,因为我们卖的时候要考虑的是一个更大的值,也就是说其实我们每次要压入的不是你枚举的值,而是比你枚举的值大的一个值,这个一来很难受,二来……我改完之后也不对……
所以还是倒着比较好,这样你枚举到一个值直接压进去就行了,比较方便。还有就是这个时候题还没做完,你会发现你的数特别大。因为前w天你是不能卖股票的,只能买入,所以一开始我们要把dp数组赋初值,你买了多少股,那么初值就是花费的价钱的负数。注意我们还要一开始把所有的值设为-INF。
之后照常DP就可以啦。
看一下代码。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') #define pr pair<int,int> #define mp make_pair #define fi first #define sc second using namespace std; typedef long long ll; const int M = 2005; const int N = 10000005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct node { int val,pos; }qa[M],qb[M]; int T,maxp,W,ap[M],bp[M],as[M],bs[M],dp[M][M],heada,taila,headb,tailb,ans; int main() { T = read(),maxp = read(),W = read(); rep(i,1,T) ap[i] = read(),bp[i] = read(),as[i] = read(),bs[i] = read(); memset(dp,-0x3f,sizeof(dp)); rep(i,1,W+1) rep(j,0,min(as[i],maxp)) dp[i][j] = -j * ap[i]; rep(i,1,T) { rep(j,0,maxp) dp[i][j] = max(dp[i][j],dp[i-1][j]); if(i <= W + 1) continue; heada = headb = 1,taila = tailb = 0; rep(j,0,maxp) { int cura = dp[i - W - 1][j] + j * ap[i]; while(heada <= taila && qa[taila].val <= cura) taila--; qa[++taila].val = cura,qa[taila].pos = j; while(heada <= taila && qa[heada].pos < max(0,j-as[i])) heada++; dp[i][j] = max(dp[i][j],qa[heada].val - j * ap[i]); } per(j,maxp,0) { int curb = dp[i - W - 1][j] + j * bp[i]; while(headb <= tailb && qb[tailb].val <= curb) tailb--; qb[++tailb].val = curb,qb[tailb].pos = j; while(headb <= tailb && qb[headb].pos > min(maxp,j+bs[i])) headb++; dp[i][j] = max(dp[i][j],qb[headb].val - j * bp[i]); } } rep(i,0,maxp) ans = max(ans,dp[T][i]); printf("%d\n",ans); return 0; }
当你意识到,每个上一秒都成为永恒。