1. 程式人生 > >SCOI2010 股票交易

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; }