[單調隊列DP] HDU3401 Trade

Trade
題意:
炒股,給出第1~t天每天的買入價格Ap[i]和賣出價格Bp[i],每天最多能買入的數量As[i]和最多能賣出的數量Bs[i]。
還有幾個限制,任意時刻最多持有Pmax數量的股票,兩次交易(買入或賣出)間隔至少w天,如第i天交易,那麼下次至少是i+w+1天。問你第t天最大收入。
題解:
首先考慮樸素DP。
DP[i][j] 表示第i天持有j股票時的最大收入。
討論轉移方程:
第i天啥也不幹:
DP[i][j]=DP[i1][j]
第i天買點,顯然有j>k
DP[i][j]=DP[iw1][k](jk)Ap[i],(jk<=As[i])
第i天賣點,顯然有k>j
DP[i][j]=DP[iw1][k]+(kj)Bp[i],(kj<=Bs[i])
然後每天能幹的事就考慮完了,接下來考慮代碼實現。

for(int i = 1; i <= t; ++i){
    for(int j = 0; j <= p; ++j){
        int mx = -inf;
        mx = max(dp[i][j], dp[i-1][j]);
        if(i <= w+1) continue;
        for(int k = j; k >= 0 && j-k <= As[i]; --k){
            mx = max(mx, dp[i-w-1][k]-(j-k)*Ap[i]);
        }
        for(int k = j; k <= p && k-j <= Bs[i]; ++k){
            mx = max(mx, dp[i-w-1][k]+(k-j)*Bp[i]);
        }
        dp[i][j] = mx;
    }
}

我寫出來是這樣的,這是個tppDP ,顯然過不了。
那就要想優化了。
第一個方程是O(1) 的就不說了。
看第二個方程。
DP[i][j]=DP[iw1][k](jk)Ap[i]
整理一下。
DP[i][j]=DP[iw1][k]+kAp[i]jAp[i]
再令f[iw1][k]=DP[iw1][k]+kAp[i]
那麼方程可以這樣轉化:
DP[i][j]=f[iw1][k]jAp[i]
那麼轉移完的結果就是
DP[i][j]=max(f[iw1][k])jAp[i],(j>k)
單調隊列就是針對這樣的方程進行優化的。
一般的,對於類似DP[i]=max/min(f[k])+C[j] ,其中Ck ,都可以使用單調隊列,效果是使O(N) 的轉移降至O(1)
代碼實現上,根據維護的信息範圍不同,遍歷的順序就不同。

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int inf = ~0u>>2;
const int N = 2005;
int dp[N][N]; // 第i天擁有j股票情況下的最大收入
// dp[i][j] = dp[i-1][j];
// dp[i][j] = dp[i-w-1][k]+k*Api-j*Api; j <= p && j-k <= Asi 買入
// dp[i][j] = dp[i-w-1][k]+k*Bpi-j*Bpi; j >= 0 k-j <= Bsi 賣出
//
// 令 f[i-w-1][k] = dp[i-w-1][k]+k*Api
// dp[i][j] = max(f[i-w-1][k]) - j*Api; j > k
//
// 令f[i-w-1][k] = dp[i-w-1][k]+k*Bpi
// dp[i][j] = max(f[i-w-1][k]) - j*Bpi j < k
int As[N], Bs[N], Ap[N], Bp[N];
int top, tail;
struct node{
    int val, pos;
    node(){}
    node(int a, int b){ val = a, pos = b;}
}q[N*10];
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        int t, p, w;
        scanf("%d%d%d", &t, &p, &w);
        for(int i = 1; i <= t; ++i) scanf("%d%d%d%d", Ap+i, Bp+i, As+i, Bs+i);
        for(int i = 0; i <= t; ++i) for(int j = 0; j <= p; ++j) dp[i][j] = -inf;
        for(int i = 1; i <= w+1; ++i){
            int up = min(p, As[i]);
            for(int j = 0; j <= up; ++j){
                dp[i][j] = -j*Ap[i];
            }
        }
        for(int i = 1; i <= t; ++i){
            top = tail = 0;
            for(int j = 0; j <= p; ++j){
                dp[i][j] = max(dp[i][j], dp[i-1][j]);
                if(i <= w+1) continue;
                int nf = dp[i-w-1][j]+j*Ap[i];
                while(top < tail && q[tail-1].val < nf) tail--;
                q[tail++] = node(nf, j);
                while(top < tail && j-q[top].pos > As[i]) top++;
                dp[i][j] = max(dp[i][j], q[top].val-j*Ap[i]);
            }
            top = tail = 0;
            for(int j = p; j >= 0; --j){
                dp[i][j] = max(dp[i][j], dp[i-1][j]);
                if(i <= w+1) break;
                int nf = dp[i-w-1][j]+j*Bp[i];
                while(top < tail && q[tail-1].val < nf) tail--;
                q[tail++] = node(nf, j);
                while(top < tail && q[top].pos-j > Bs[i]) top++;
                dp[i][j] = max(dp[i][j], q[top].val-j*Bp[i]);
            }
        }
        int ans = 0;
        for(int i = 0; i <= p; ++i) ans = max(ans, dp[t][i]);
        printf("%d\n", ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章