Trade HDU - 3401 (单调队列优化dp)

思路:首先把dp状态方程写出来:设dp[i][j]表示第i天有j个股票的最大值。

那么转移可以有三种情况:在第i+1天什么都不干,dp[i+1][j]=dp[i][j]

在买进一些股票:dp[i+1][j]=max(dp[i+1][j],dp[i+1-w-1][k]-(j-k)*ap

在卖出一些股票:dp[i+1][j]=max(dp[i+1-w-1][k]+(j-k)*bp)

可以发现如果直接写的话,复杂度必然是O(n^3)

如何优化?我们发现每次我们都要枚举k,找出最优的k值来,有没有方法可以让我们直接求出最优的k?

有一点可以确信:dp[i+1][j]>=dp[i][j],因为我不可能在没有改变现有股票的情况下去失去一些利益。换句话说,当j一定时,dp[i][j]是关于i的单调递增函数。

换句话说,dp[i-w][k]完全可以保存下来,当下次需要找dp[i-w]时的最大值时,就可以直接查找。

如何实现? 利用单调队列记录一个单调递减的序列,每次找队首即可。

在具体实现的时候,还要注意有每次买卖的上限。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e3+7;
const int mod=1e9+7;
int tim,Mp,w;
struct Point
{
    int AP,BP,AS,BS;
}p[maxn];
int dp[maxn][maxn];
deque<int> q;
int cal1(int i,int j)
{
    return dp[i-w][j]+p[i].AP*j;
}
int cal2(int i,int j)
{
    return dp[i-w][j]+p[i].BP*j;
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d%d",&tim,&Mp,&w);
        w++;
        for(int i=1;i<=tim;i++)
        {
            scanf("%d%d%d%d",&p[i].AP,&p[i].BP,&p[i].AS,&p[i].BS);
        }
        for(int i=1;i<=tim;i++)  //初始化
        {
            dp[i][0]=0;
            for(int j=1;j<=Mp;j++)
            {
                dp[i][j]=-inf;
            }
        }
        for(int i=1;i<=w;i++) //预处理第一天的
        {
            for(int j=1;j<=p[i].AS;j++)
            {

                dp[i][j]=-j*p[i].AP;
            }
        }
        int i;
        for(i=2;i<=w;i++)
        {
            for(int j=1;j<=Mp;j++)
            {
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
            }
        }
        int zt=0;
        for(;i<=tim;i++)
        {
            while(q.empty()==0) q.pop_back();//每次都清空队列
            for(int j=0;j<=Mp;j++)
            {
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
                zt=dp[i-w][j]+j*p[i].AP;
                while(q.empty()==0&&cal1(i,q.back())<zt)
                {
                    q.pop_back();
                }
                q.push_back(j);
                while(q.empty()==0&&j-q.front()>p[i].AS) //维护上限
                {
                    q.pop_front();
                }
                if(q.empty()==0)
                {
                    dp[i][j]=max(dp[i][j],cal1(i,q.front())-j*p[i].AP);
                }   
            }
            while(q.empty()==0) q.pop_back();//减法反着来
            for(int j=Mp;j>=0;j--)
            {
                zt=cal2(i,j);
                while(q.empty()==0&&zt>cal2(i,q.back()))
                {
                    q.pop_back();
                }
                q.push_back(j);
                while(q.empty()==0&&q.front()-j>p[i].BS)
                {
                    q.pop_front();
                }
                if(q.empty()==0)
                {
                    dp[i][j]=max(dp[i][j],cal2(i,q.front())-j*p[i].BP);
                }
            }
        }
        int ans=-inf;
        for(i=0;i<=Mp;i++)
        {
            ans=max(dp[tim][i],ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/82934507