1. 程式人生 > >HDU-3401-Trade-dp-單調佇列優化

HDU-3401-Trade-dp-單調佇列優化

【Description】

Recently, lxhgww is addicted to stock, he finds some regular patterns after a few
days' study.
He forecasts the next T days' stock market. On the i'th day, you can buy one stock
with the price APi or sell one stock to get BPi.
There are some other limits, one can buy at most ASi stocks on the i'th day and at
most sell BSi stocks.
Two trading days should have a interval of more than W days. That is to say,suppose
you traded (any buy or sell stocks is regarded as a trade)on the i'th day, the next
trading day must be on the (i+W+1)th day or later.
What's more, one can own no more than MaxP stocks at any time.

Before the first day, lxhgww already has infinitely money but no stocks, of course
he wants to earn as much money as possible from the stock market. So the question
comes, how much at most can he earn? 

【Input】

The first line is an integer t, the case number.
The first line of each case are three integers T , MaxP , W .
(0 <= W < T <= 2000, 1 <= MaxP <= 2000) .
The next T lines each has four integers APi,BPi,ASi,BSi
( 1<=BPi<=APi<=1000,1<=ASi,BSi<=MaxP), which are mentioned above. 

【Output】

The most money lxhgww can earn. 

【Examples 】

Sample Input

1
5 2 0
2 1 1 1
2 1 1 1
3 2 1 1
4 3 1 1
5 4 1 1

Sample Output

3

【Problem Description】

 知道未來T天的股票買入價格和賣出價格,在滿足以下條件下求所能獲得的最大收益。
 1、一次買入或賣出操作後,必須間隔W天才能進行第二次操作。
 2、每天買入和賣出的股票數有限制。

【Solution】

 dp,單調佇列優化
 定義dp陣列dp[i][j],表示第i天買j只股票所能獲得的最大收益。
 則買入的狀態轉移方程:dp[i][j]=max(dp[i][j],dp[i-W-1][k]-(j-k)*Ap[i])
                             =max(dp[i][j],dp[i-W-1][k]+k*Ap[i]-j*Ap[i])
   賣出的狀態轉移方程:dp[i][j]=max(dp[i][j],dp[i-W-1][k]+(k-j)*Bp[i])
                             =max(dp[i][j],dp[i-W-1][k]+k*Bp[i]-j*Bp[i])
 無論是買入或賣出,dp[i-W-1][k]+k*Xp[i]是跟k相關的值,j*Xp[i]是與j相關的值
 而且題目要求是最大收益,對於同一個j,列舉所有的k求dp[i-W-1][k]+k*Xp[i]的最大
 值就好了,那麼就可以將這個式子通過單調佇列來維護。
 注意最後求值的時候,還要保證abs(k-j)<=Xs[i],即買入或賣出的數量不超過限制。

【Code】

/*
 * @Author: Simon
 * @Date: 2018-08-17 09:12:27
 * @Last Modified by:   Simon
 * @Last Modified time: 2018-08-17 09:35:57
 */
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 2015
int Api[maxn]; int Bpi[maxn];
int Asi[maxn]; int Bsi[maxn];
int f[maxn][maxn];
int head,tail;
int T, MaxP, W;
struct node
{
    int val,num;
}q[maxn*2];
void buy(int i,int j)
{
    while(head<tail&&q[tail-1].val<f[i-W-1][j]+j*Api[i]) tail--;//dp[i][k]+k*Api[i];
    q[tail].val=f[i-W-1][j]+j*Api[i];
    q[tail++].num=j;
    while(head==-1||(head<tail&&j-q[head].num>Asi[i])) head++;//保證買的數目不超過限制
    f[i][j]=max(f[i][j],q[head].val-j*Api[i]);//dp[i][j]=max(dp[i][j],dp[i][k]-(j-k)*Api[i])
}
void sale(int i,int j)
{
    while(head<tail&&q[tail-1].val<f[i-W-1][j]+j*Bpi[i]) tail--;//dp[i][k]+k*Bpi[i]
    q[tail].val=f[i-W-1][j]+j*Bpi[i];
    q[tail++].num=j;
    while(head==-1||(head<tail&&q[head].num-j>Bsi[i])) head++;//保證賣的數目不超過限制
    f[i][j]=max(f[i][j],q[head].val-j*Bpi[i]);//dp[i][j]=max(dp[i][j],dp[i][k]+(k-j)*Bpi[i])
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        cin >> T >> MaxP >> W;
        for (int i = 1; i <= T; i++) cin >> Api[i] >> Bpi[i] >> Asi[i] >> Bsi[i];
        for(int i=0;i<=T;i++)
            for(int j=0;j<=MaxP;j++) f[i][j]=-1e8;

        for(int i=1;i<=T;i++) f[i][0]=0;//任意一天不買不賣所獲得的收益為0
        for(int i=1;i<=W+1;i++)
        {
            for(int j=0;j<=Asi[i];j++)
            {
                f[i][j]=-j*Api[i];//前w+1天,只能買進
            }
        }
        f[0][0]=0;
        for(int i=2;i<=T;i++)
        {
            head=0,tail=0;
            for(int j=0;j<=MaxP;j++) f[i][j]=max(f[i-1][j],f[i][j]);//不買不賣時的最大值
            if(i<=W+1) continue;
            for(int j=0;j<=MaxP;j++) buy(i,j);//因為是買進,所以比應該正序,保證比當前買的數目小的狀態都已算過
            head=0,tail=0;
            for(int j=MaxP;j>=0;j--) sale(i,j);//相反...
        }
        int ans=-1e8;
        for(int j=0;j<=MaxP;j++) ans=max(ans,f[T][j]);
        cout<<ans<<endl;
    }
    cin.get(), cin.get();
    return 0;
}