您的位置:首页 > 其它

股票交易 【SCOI 2010】HDU 3401 (单调队列优化DP入门)

2017-10-25 16:58 323 查看
题目描述:

 最近 Lxhgww 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。

 通过一段时间的观察,Lxhgww 预测到了未来 T 天内某只股票的走势,第 i 天的股票买入价为每股 APi,第 i 天的股票卖出价为每股 BPi数据保证对于每个i,都有APi≥BPi),但是每天不能无限制地交易,于是股票交易所规定第 i 天的一次买入至多只能购买ASi 股,一次卖出至多只能卖出 BSi 股。

 另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔 W 天,也就是说如果在第 i 天发生了交易,那么从第 i+1 天到第 i+W 天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过 MaxP 。

 在第 1 天之前,Lxhgww 手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T 天以后,Lxhgww 想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?

输入:

 输入数据第一行包括3个整数,分别是 T,MaxP,W 。

 接下来 T 行,第 i 行代表第 i-1 天的股票走势,每行 4 个整数,分别表示 APi,BPi,ASi,BSi 。

输出:

 输出数据为一行,包括 1 个数字,表示 Lxhgww 能赚到的最多的钱数。

样例输入:

5 2 0

2 1 1 1

2 1 1 1

3 2 1 1

4 3 1 1

5 4 1 1

样例输出:

3

【数据范围】

对于 30% 的数据,0≤W<T≤50;1≤MaxP≤50 ;

对于 50% 的数据,0≤W<T≤2000;1≤MaxP≤50 ;

对于 100% 的数据,0≤W<T≤2000;1≤MaxP≤2000 ;

对于所有的数据,1≤BPi≤APi≤1000;1≤ASi,BSi≤MaxP 。

题目分析:

 读完题,我们可以根据题意将每天的情况分为3类,分别是:

  ①、及不买也不卖

  ②、买进一些股票

  ③、卖出一些股票

 

 因为求解最优解,我们可以定义 dp[ i ][ j ]表示第 i 天,有 j 只股票的最优解,同样分三种情况来讨论转移方程(分别对应上面三种情况):

  ①、dp[ i ][ j ] = dp[ i - 1 ][ j ]

  ②、dp[ i ][ j ] = max( dp[ i ][ j ] , dp[ r ][ k ] - AS[ i ] * ( j-k ) )

  ③、dp[ i ][ j ] = max( dp[ i ][ j ] , dp[ r ][ k ] +BS[ i ]* ( k -j ) )

 这个转移 ,我们每次要枚举 i , j , r , k 才能转移,考虑降维度;

 

 首先我们想到,因为 r 必须满足 r<=i-w-1,对于i-w-1之前都可以通过方程①转移到 i-w-1天,所以我们就不需要枚举 r 转移了,每次记录一个前缀最大值即可;

 然后我们将方程②拆开:dp[ i ][ j ] =max(dp[ i ][ j] , dp[ i-w-1 ][ k ]+k*AS[i]-AS[i] * j,我们发现 dp[ i-w-1 ][ k ]+k*AS[i] 是一个与 j 完全无关的量,那么我们就可以每次计算的时候将 dp[ i-w-1 ][ k ]+k*AS[i] ,加入一个单调队列里维护对首,对于队尾小于它的就可以直接弹出队列,因为那些数答案既没有它优,而且对后面状态的影响更小,然后每次更新 dp[i][j]是就可以直接取出对首更新。

 

 这样转移变成了O(1)的,总时间复杂度为O(n2),可以解决这道题;

实际上单调队列优化DP就是像本题中找到一个与‘ j ’ (不同的题不同) 无关的量用单调队列维护,每次取出对首即为最优解

#include<bits/stdc++.h>
using namespace std;
#define inf 0xfffffff
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b

struct node
{
int pos;
int f;
};

int AP[2001],BP[2001],AS[2001],BS[2001];
int dp[2001][2001];
node q[2001];
int head,tail;
int n,MaxP,W;

int main()
{
int i,j,cas,nowf;
scanf("%d%d%d",&n,&MaxP,&W);
for(i=1;i<=n;i++)
{
scanf("%d%d%d%d",&AP[i],&BP[i],&AS[i],&BS[i]);
}
for(i=0;i<=n;i++)
for(j=0;j<=MaxP;j++)
dp[i][j]=-inf;
for(i=1;i<=W+1;i++)
{
for(j=0;j<=(min(MaxP,AS[i]));j++)
dp[i][j]=-AP[i]*j;
}
dp[0][0]=0;
for(i=1;i<=n;i++)
{
for(j=0;j<=MaxP;j++)
dp[i][j]=max(dp[i][j],dp[i-1][j]);
if(i<=W+1)
continue;
int pre=i-W-1; //队列里保存的是第i-W-1天的信息
head=tail=0;
for(j=0;j<=MaxP;j++) //因为是买,所以肯定越来越多,求DP[i][j]的时候,之前的0~~j-1的信息就存在在队列了
{
nowf=dp[pre][j]+j*AP[i];
while(head<tail && q[tail-1].f<nowf)
tail--;
q[tail].f=nowf;q[tail++].pos=j;
while(head<tail && q[head].pos+AS[i]<j)
head++;
dp[i][j]=max(dp[i][j],q[head].f-j*AP[i]);
}
head=tail=0;
for(j=MaxP;j>=0;j--) //因为是卖,道理同上
{
nowf=dp[pre][j]+j*BP[i];
while(head<tail && q[tail-1].f<nowf)
tail--;
q[tail].f=nowf;q[tail++].pos=j;
while(head<tail && q[head].pos-BS[i]>j)
head++;
dp[i][j]=max(dp[i][j],q[head].f-j*BP[i]);
}
}
int ans=0;
for(i=0;i<=MaxP;i++)
ans=max(ans,dp
[i]);
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: