hdu 3401 單調隊列優化dp

Trade

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3588    Accepted Submission(s): 1161


Problem 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.
 

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
 

Author
lxhgww
 

Source
 


提交情況:WA WA WA WA WA WA WA WA WA WA WA ....AC

題目大意:
有個人買賣股票來賺錢,
給出一個交易區段的天數t , 有一個可以購買的股票數量上限maxp,並且當前交易與上一次交易必須間隔W天
接着會給出每天的股票的買入賣出價格ap[i],bp[i] , 接着每天買入賣出的股票數量限制as[i],bs[i]。
求到了第t天可以獲得的最大利潤。

解題思路:
總的思路是
前W+1天【1,W+1】,只能夠進行兩種狀態轉移:j>0時從前一天直接轉移,不買不賣;還有從前一天的j==0轉移到今天的j>0,
也就是今天第一次買股票,買了j支。
剩餘的時間,除了以上兩種狀態轉移之外,還可以有從第i-W-1天的已有的j-k支股票的前提下,再買入k支股票。

此外還需要注意初始化的問題,
(1)一開始做的時候將dp全都初始化爲了0,這樣的話結果一定是錯的,因爲dp小於0的時候賣出股票之後收益可能會比dp=0的時候要大。
正確的做法應該是賦值爲無窮小。
(2)還有,dp[1][0]必然就是0的了,前W+1天的dp[i][0]都是0,因爲絕對不可能進行賣出交易。而後面的dp[i][0]就不一定是零,可能是剛剛把股票賣完了。


反省一下,wa了這麼多次,主要還是因爲自己的下標處理能力太差。出現的錯誤有head和tail的初始化寫反了,還有上面說的初始化的問題,
還有後來優先隊列刪除末尾節點的地方把tail寫成了head。還有一個自覺得很重要的錯誤,在第二份ac代碼中會有標出。



第一遍先用普通樸素的dp做了一遍,T了
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 2015

using namespace std;
int ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][maxn];
int n,maxp,W,ans;

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        memset(dp,0,sizeof(dp));
        ans=0;
        scanf("%d %d %d",&n,&maxp,&W);
        for(int i=1;i<=n;i+=1){
            scanf("%d %d %d %d",&ap[i],&bp[i],&as[i],&bs[i]);
        }

        for(int i=1;i<=maxp;i+=1){
            dp[1][i]=dp[1][i-1]-ap[i];
        }
        for(int i=2;i<=n;i+=1){
            for(int j=0;j<=maxp;j+=1){
                dp[i][j]=max(dp[i-1][0]-j*ap[i],dp[i-1][j]);
                if(i>W+1){
                    //dp[i][j]=max(dp[i-1][0]-j*ap[i],dp[i-1][j]);
                    for(int k=1;k<=j&&k<=as[i];k+=1){ /*買進*/
                    //for(int k=1;j+k<=maxp&&k<=as[i];k+=1){
                        dp[i][j]=max(dp[i][j],dp[i-W-1][j-k]-ap[i]*k);
                        //dp[i][j]=max(dp[i][j],dp[i-W-1][j+k]-ap[i]*k);
                    }
                    for(int k=1;j+k<=maxp&&k<=bs[i];k+=1){  /*賣出*/
                    //for(int k=1;k<=j&&k<=bs[i];k+=1){
                        dp[i][j]=max(dp[i][j],dp[i-W-1][j+k]+bp[i]*k);
                        //dp[i][j]=max(dp[i][j],dp[i-W-1][j-k]+bp[i]*k);
                    }
                }
                //else{
                    //dp[i][j]=dp[i-1][j];
                    //dp[i][j]=max(dp[i-1][j],dp[i-1][0]-j*ap[i]);
                //}

                if(i==n){
                    ans=max(ans,dp[i][j]);
                }
            }
        }

        printf("%d\n",/*dp[n][0]*/ans);
    }
    return 0;
}

第二遍,用單調隊列優化,wa了很多,但最後過了(下面是註釋版和簡明版兩個版本):
<pre name="code" class="cpp">#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 2052

using namespace std;
int ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][maxn];
int q[maxn],J[maxn];
int n,maxp,W,ans;

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        memset(dp,128,sizeof(dp));
        //原來錯了,因爲沒有初始化無窮小,這裏也導致了8到3的蛻變
        //想想在i=n-1的某種情況下,從q裏取出的值暫時是負的,
        //如果初始化爲0這個時候就就不會選擇負的數了
        ans=0;
        scanf("%d %d %d",&n,&maxp,&W);
        for(int i=1;i<=n;i+=1){
            scanf("%d %d %d %d",&ap[i],&bp[i],&as[i],&bs[i]);
        }

        /*dp[1][0]=0;
        for(int i=1;i<=maxp;i+=1){
            dp[1][i]=dp[1][i-1]-ap[1];
        }*/
        dp[0][0]=0;
        for(int i=1;i<=W+1;i+=1){
            for(int j=0;j<=maxp&&j<=as[i];j+=1){ //因爲是前W+1天只能買一次,所以必須加上一個j<=as[i]的限制
                dp[i][j]=-j*ap[i];
            }
        }
        for(int i=1;i<=n;i+=1){
            for(int j=0;/*i>=W+2&&*/j<=maxp;j+=1){
            //這裏不能有註釋掉的語句,因爲雖然當天不能賣那麼多的股票,但是可以從昨天轉移過來
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
            }
            if(i<W+2) continue;    //因爲這裏沒有continue會re
            int tail=0,head=1;      //這裏有錯原來是  int tail=1,head=0; 發生了從8到3的蛻變
            for(int j=0;j<=maxp;j+=1){  /*買進*/
                while(/*j-J[head]<as[i]*/J[head] + as[i] < j&&head<=tail) head+=1;  //維護單調隊列的長度
                int newdp=dp[i-W-1][j]+j*ap[i];
                while(head<=tail&&newdp>q[tail]) tail-=1;
                tail+=1;
                q[tail]=newdp,J[tail]=j;
                //j[tail]這一步很重要,因爲是多重揹包,每一次入貨出貨都有限制,
                //而由於使用單調隊列取得的最優解不一定符合限制條件,會過時

                //while(/*j-J[head]<as[i]*/J[head] + as[i] < j&&head<=tail) head+=1;  //維護單調隊列的長度
                if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*ap[i]);
            }

            tail=0,head=1;//這裏有錯原來是  int tail=1,head=0;
            for(int j=maxp;j>=0;j-=1){  /*賣出*/
                while(/*J[head]-j<bs[i]*/J[head] - bs[i] > j&&head<=tail) head+=1;  //維護單調隊列的長度
                int newdp=dp[i-W-1][j]+j*bp[i];
                while(head<=tail&&newdp>q[tail]) tail-=1;
                tail+=1;
                q[tail]=newdp,J[tail]=j;
                //j[tail]這一步很重要,因爲是多重揹包,每一次入貨出貨都有限制,
                //而由於使用單調隊列取得的最優解不一定符合限制條件,會過時

                //while(/*J[head]-j<bs[i]*/J[head] - bs[i] > j&&head<=tail) head+=1;  //維護單調隊列的長度
                if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*bp[i]);
            }

            if(i==n){
                for(int j=0;j<=maxp;j+=1){
                    ans=max(ans,dp[n][j]);
                }
            }
        }

        printf("%d\n",/*dp[n][0]*/ans);
    }
    return 0;
}




簡明版
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 2052

using namespace std;
int ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][maxn];
int q[maxn],j[maxn];
int n,maxp,w,ans;

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        memset(dp,128,sizeof(dp));
        ans=0;
        scanf("%d %d %d",&n,&maxp,&w);
        for(int i=1;i<=n;i+=1){
            scanf("%d %d %d %d",&ap[i],&bp[i],&as[i],&bs[i]);
        }

        dp[0][0]=0;
        for(int i=1;i<=w+1;i+=1){
            for(int j=0;j<=maxp&&j<=as[i];j+=1){
                dp[i][j]=-j*ap[i];
            }
        }
        for(int i=1;i<=n;i+=1){
            for(int j=0;j<=maxp;j+=1){
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
            }
            if(i<w+2) continue;
            int tail=0,head=1;
            for(int j=0;j<=maxp;j+=1){
                while(j[head] + as[i] < j&&head<=tail) head+=1;
                int newdp=dp[i-w-1][j]+j*ap[i];
                while(head<=tail&&newdp>q[tail]) tail-=1;
                tail+=1;
                q[tail]=newdp,j[tail]=j;
                if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*ap[i]);
            }

            tail=0,head=1;
            for(int j=maxp;j>=0;j-=1){
                while(j[head] - bs[i] > j&&head<=tail) head+=1;
                int newdp=dp[i-w-1][j]+j*bp[i];
                while(head<=tail&&newdp>q[tail]) tail-=1;
                tail+=1;
                q[tail]=newdp,j[tail]=j;
                if(head<=tail) dp[i][j]=max(dp[i][j],q[head]-j*bp[i]);
            }

            if(i==n){
                for(int j=0;j<=maxp;j+=1){
                    ans=max(ans,dp[n][j]);
                }
            }
        }

        printf("%d\n",/*dp[n][0]*/ans);
    }
    return 0;
}


發佈了91 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章