[Scoi2010]股票交易(dp的不断优化/单调队列优化dp)

传送门

题意:直接看题意即可

题解:首先,第一步可以想到的是dp,用dp[i][j]表示在第i天下有j张票的最大钱数,然后按照时间顺序dp下去,复杂度o(t^2mp^2),只能得到40分。

附上代码:


#include<bits/stdc++.h>

using namespace std;

const int MAX=2e3+50;

int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];

int main()
{
    scanf("%d%d%d",&t,&mp,&w);
    for(int i=1;i<=t;i++){
        scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
    }
    memset(dp,-63,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=t;i++){
        for(int j=0;j<=max(0,i-w-1);j++){
            for(int k=0;k<=mp;k++){
                for(int l=0;k+l<=mp&&l<=lb[i];l++){
                    dp[i][k+l]=max(dp[i][k+l],dp[j][k]-l*vb[i]);
                }
                for(int l=0;l<=k&&l<=ls[i];l++){
                    dp[i][k-l]=max(dp[i][k-l],dp[j][k]+l*vs[i]);
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=t;i++){
        ans=max(ans,dp[i][0]);
    }
    cout<<ans<<endl;
    return 0;
}

其实没必要枚举限制天数,这样想,dp[i][j]是在第i天拥有j个股票的最大获利,那么直接可以递推前一天dp[i][j]=max(dp[i][j],dp[i-1][j])即可,这样可以拿到50分

附上代码:


#include<bits/stdc++.h>

using namespace std;

const int MAX=2e3+50;

int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];

int main()
{
    scanf("%d%d%d",&t,&mp,&w);
    for(int i=1;i<=t;i++){
        scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
    }
    memset(dp,-63,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=t;i++){
        int j=max(0,i-w-1);
        for(int k=0;k<=mp;k++){
            dp[i][k]=max(dp[i][k],dp[i-1][k]);
            for(int l=0;k+l<=mp&&l<=lb[i];l++){
                dp[i][k+l]=max(dp[i][k+l],dp[j][k]-l*vb[i]);
            }
            for(int l=0;l<=k&&l<=ls[i];l++){
                dp[i][k-l]=max(dp[i][k-l],dp[j][k]+l*vs[i]);
            }
        }
    }
    cout<<dp[t][0]<<endl;
    return 0;
}

其实还可以发现,在前W天只能买或者不买,那么再优化一下,就可以拿到70分了

附上代码:


#include<bits/stdc++.h>

using namespace std;

const int MAX=2e3+50;

int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];

int main()
{
    scanf("%d%d%d",&t,&mp,&w);
    for(int i=1;i<=t;i++){
        scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
    }
    memset(dp,-63,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=t;i++){
        for(int j=0;j<=lb[i];j++){
            dp[i][j]=-j*vb[i];
        }
        for(int j=0;j<=mp;j++){
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
        }
        if(i<=w){
            continue;
        }
        int j=max(0,i-w-1);
        for(int k=0;k<=mp;k++){
            dp[i][k]=max(dp[i][k],dp[i-1][k]);
            for(int l=0;k+l<=mp&&l<=lb[i];l++){
                dp[i][k+l]=max(dp[i][k+l],dp[j][k]-l*vb[i]);
            }
            for(int l=0;l<=k&&l<=ls[i];l++){
                dp[i][k-l]=max(dp[i][k-l],dp[j][k]+l*vs[i]);
            }
        }
    }
    cout<<dp[t][0]<<endl;
    return 0;
}

最后可以发现,如果买股票的话,对于每个dp[i][j]=max(dp[x][k]-(j-k)*v),展开为dp[i][j]=max(dp[x][k]+k*v-j*v),又可以发现j*v始终是j*v,所以可以移动外面,dp[i][j]=max(dp[x][k]+k*v)-j*v,所以此时通过单调队列进行维护max(dp[x][k]+k*v),同理,如果卖股票的话,dp[i][j]=max(dp[x][k]+(k-j)*v),展开dp[i][j]=max(dp[x][k]+k*v-j*v),发现j*v没关系,转到外面,dp[i][j]=max(dp[x][k]+k*v)+j*v,然后,还是通过单调队列进行维护max(dp[x][k]+k*v),而此时的方向需要从后往前进行维护。

附上代码:


#include<bits/stdc++.h>

using namespace std;

const int MAX=2e3+50;

int t,mp,w;
int dp[MAX][MAX];
int vb[MAX],vs[MAX],lb[MAX],ls[MAX];

int deq[MAX];

int main()
{
    scanf("%d%d%d",&t,&mp,&w);
    for(int i=1;i<=t;i++){
        scanf("%d%d%d%d",&vb[i],&vs[i],&lb[i],&ls[i]);
    }
    memset(dp,-63,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=t;i++){
        for(int j=0;j<=lb[i];j++){
            dp[i][j]=-j*vb[i];
        }
        for(int j=0;j<=mp;j++){
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
        }
        if(i<=w){
            continue;
        }
        int x=i-w-1,head=0,tail=0;
        for(int j=0;j<=mp;j++){
            while(head<tail&&deq[head]<j-lb[i]){
                head++;
            }
            while(head<tail&&dp[x][deq[tail-1]]+deq[tail-1]*vb[i]<=dp[x][j]+j*vb[i]){
                tail--;
            }
            deq[tail++]=j;
            if(head<tail){
                dp[i][j]=max(dp[i][j],dp[x][deq[head]]+deq[head]*vb[i]-j*vb[i]);
            }
        }
        head=tail=0;
        for(int j=mp;j>=0;j--){
            while(head<tail&&deq[head]>j+ls[i]){
                head++;
            }
            while(head<tail&&dp[x][deq[tail-1]]+deq[tail-1]*vs[i]<=dp[x][j]+j*vs[i]){
                tail--;
            }
            deq[tail++]=j;
            if(head<tail){
                dp[i][j]=max(dp[i][j],dp[x][deq[head]]+deq[head]*vs[i]-j*vs[i]);
            }
        }
    }
    cout<<dp[t][0]<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/85374213
今日推荐