此题链接到dp常见优化方法

开始的时候被纪念品误导,以为是多支股票,后来发现事情不妙:

这道题知道的是某一只股票的走势;

\(Solution\):

\(70pts\):

设\(f[i][j]\)表示第i天持有j股股票可以获得的最大利润,有如下转移方程:

\(f[i][j]=Max\begin{cases}f[i-1][j] \ \ \ \ //第i天不买也不卖 \\-Ap[i]*j\ \ \ // 第i天之前不持有股票,第i天买入j股股票 \\f[i-w-1][k]-Ap[i]*(j-k) \ \ \ Max(0,j-As[i])\leq k< j \ \ //第i天买入j-k股股票 \\f[i-w-1][k]+Bp[i]*(k-j)\ \ \ j<k\leq Min(MaxP,j+Bs[i]);\ \ //第i天卖出k-j股股票 \end{cases}\)

为什么转移是第\(i-w-1\)天而不是第\(i-w-2\)亦或其它日子呢?

借用一段话(原文

看第一种情况(第i天不买也不卖),我们已经把某一天以前的最优答案转移到了该天,所以从那一天转移,相当于从那一天包括前面任何一天开始转移,省去了大把时间。

利用上面的四个式子就可以写出代码,注意初始化的时候要将\(f[ \ ][ \ ]\)初始化为一个很小的数,\(f[0][0]=0\)

\(Code:\)

#include<bits/stdc++.h>

using namespace std;

inline int read() {
int ans=0;
char last=' ',ch=getchar();
while(ch>'9'||ch<'0') last=ch,ch=getchar();
while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
if(last=='-') ans=-ans;
return ans;
}const int mxt=5010; int T,MaxP,w;
int Ap[mxt],Bp[mxt],As[mxt],Bs[mxt];
int f[mxt][mxt]; int main() {
T=read();
MaxP=read();
w=read();
for(int i=1;i<=T;i++) {
Ap[i]=read();
Bp[i]=read();
As[i]=read();
Bs[i]=read();
}
memset(f,0x9f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=T;i++) {
for(int j=0;j<=MaxP;j++) {
f[i][j]=f[i-1][j];
if(i-w-1>=0) {
for(int k=max(0,j-As[i]);k<j;k++)
f[i][j]=max(f[i][j],f[i-w-1][k]-Ap[i]*(j-k));
for(int k=j+1;k<=min(MaxP,j+Bs[i]);k++) {
f[i][j]=max(f[i][j],f[i-w-1][k]+Bp[i]*(k-j));
}
}
if(j<=As[i])
f[i][j]=max(f[i][j],-Ap[i]*j);
printf("%d ",f[i][j]);
}
puts("");
}
printf("%d",f[T][0]);
return 0;
}

但不要以为这样就可以愉快的\(\mathfrak{AC}\)这道题,它毕竟是一道省选题\(\color{white}{(虽然年代已经很久远了}\)

实际复杂度\(O(T*MaxP^2)\),在\(T\)和\(MaxP\)都是\(2000\)的数量级的数据下,显然是过不去的(不要妄想跑过\(8e9\)

因此我们需要优化

看数据范围\(2000\)大致可以猜测是一个\(O(n^2)\)的代码

观察第\(3、4\)个式子,暴力拆解得到:

\(f[i][j]=Max\{f[i-w-1][k]-Ap[i]*j+Ap[i]*k\}\)

\(f[i][j]=Max\{f[i-w-1][k]-Bp[i]*j+Bp[i]*k\}\)

显然\(-Bp[i]*j\)与\(-Ap[i]*j\)的值不影响我们取最大值,因此我们可以提出来:

\(f[i][j]=Max\{f[i-w-1][k]+Ap[i]*k\}-Ap[i]*j\)

\(f[i][j]=Max\{f[i-w-1][k]+Bp[i]*k\}-Bp[i]*j\)

这两个式子像什么?像极了单调队列优化!

所以采用单调队列优化我们的第\(3、4\)个式子,就可以做到\(O(n^2)\)完成这道题(胜利的曙光\(\color{GOld}{!!!}\)

	h=1;t=0;
for(int j=0;j<=MaxP;j++) {//购买股票,从小往大更新
while(h<=t&&q[h]<j-As[i])
//清除队首中不符合"第i天的一次买入至多只能购买AS_i股"的
h++;
while(h<=t&&cost1(i,q[t])<=cost1(i,j))
//如果队尾的收入<=当前的收入,弹出队尾(从前往后更新,i要大于q[t],如果队尾的收入<=当前的收入,显然i相比于q[t]一定更优)
t--;
q[++t]=j;
if(h<=t) //队列中有元素
f[i][j]=max(f[i][j],cost1(i,q[h])-Ap[i]*j);
}
h=1;t=0;
for(int j=MaxP;j>=0;j--) {//卖出股票,从大往小更新:由4式知,j位置的状态是从一个>j的位置转移而来的,因此倒序更新
while(h<=t&&q[h]>j+Bs[i])
h++;
while(h<=t&&cost2(i,q[t])<=cost2(i,j))
t--;
q[++t]=j;
if(h<=t)
f[i][j]=max(f[i][j],cost2(i,q[h])-Bp[i]*j);
}

\(Code:\)

#include<bits/stdc++.h>

using namespace std;

inline int read() {
int ans=0;
char last=' ',ch=getchar();
while(ch>'9'||ch<'0') last=ch,ch=getchar();
while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
if(last=='-') ans=-ans;
return ans;
}const int mxt=5010; int T,MaxP,w;
int Ap[mxt],Bp[mxt],As[mxt],Bs[mxt];
int f[mxt][mxt];
int q[mxt];
int h,t; int cost1(int i,int j) {
return f[i-w-1][j]+Ap[i]*j;
} int cost2(int i,int j) {
return f[i-w-1][j]+Bp[i]*j;
} int main() {
T=read();
MaxP=read();
w=read();
for(int i=1;i<=T;i++) {
Ap[i]=read();
Bp[i]=read();
As[i]=read();
Bs[i]=read();
}
memset(f,0x9f,sizeof(f));
f[0][0]=0; for(int i=1;i<=T;i++) {
for(int j=0;j<=MaxP;j++) {
f[i][j]=f[i-1][j];
if(j<=As[i])
f[i][j]=max(f[i][j],-Ap[i]*j);
}
if(i-w<=0) continue;
h=1;t=0;
for(int j=0;j<=MaxP;j++) {
while(h<=t&&q[h]<j-As[i]) h++;
while(h<=t&&cost1(i,q[t])<=cost1(i,j)) t--;
q[++t]=j;
if(h<=t)
f[i][j]=max(f[i][j],cost1(i,q[h])-Ap[i]*j);
}
h=1;t=0;
for(int j=MaxP;j>=0;j--) {
while(h<=t&&q[h]>j+Bs[i]) h++;
while(h<=t&&cost2(i,q[t])<=cost2(i,j)) t--;
q[++t]=j;
if(h<=t)
f[i][j]=max(f[i][j],cost2(i,q[h])-Bp[i]*j);
}
}
printf("%d",f[T][0]);
return 0;
}

Luogu P2569 [SCOI2010] 股票交易的更多相关文章

  1. [luogu] P2569 [SCOI2010]股票交易 (单调队列优化)

    P2569 [SCOI2010]股票交易 题目描述 最近 \(\text{lxhgww}\) 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,\(\te ...

  2. LUOGU P2569 [SCOI2010]股票交易(单调队列优化dp)

    传送门 解题思路 不难想一个\(O(n^3)\)的\(dp\),设\(f_{i,j}\)表示第\(i\)天,手上有\(j\)股的最大收益,因为这个\(dp\)具有单调性,所以\(f_i\)可以贪心的直 ...

  3. 洛谷P2569 [SCOI2010]股票交易

    P2569 [SCOI2010]股票交易 题目描述 最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,lxhgww预测到了未来T天内某只股 ...

  4. P2569 [SCOI2010]股票交易 dp 单调队列优化

    LINK:股票交易 题目确实不算难 但是坑点挺多 关于初值的处理问题我就wa了两次. 所以来谢罪. 由于在手中的邮票的数量存在限制 且每次买入卖出也有限制. 必然要多开一维来存每天的邮票数量. 那么容 ...

  5. P2569 [SCOI2010]股票交易

    最近 \(lxhgww\) 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,\(lxhgww\) 预测到了未来 \(T\) 天内某只股票的走势,第 \( ...

  6. 洛谷 P2569[SCOI2010]股票交易(动规+单调队列)

    //只能写出裸的动规,为什么会有人能想到用单调队列优化Orz 题目描述 最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,lxhgww预测 ...

  7. Luogu 2569 [SCOI2010]股票交易 (朴素动规转移 + 单调队列优化)

    题意: 已知未来 N 天的股票走势,第 i 天最多买进 as [ i ] 股每股 ap [ i ] 元,最多卖出 bs [ i ] 股每股 bp [ i ] 元,且每天最多拥有 Mp 股,且每两次交易 ...

  8. 洛谷P2569 [SCOI2010]股票交易(单调队列)

    传送门 惭愧……这种题目都没看出来…… 首先,我们用$dp[i][j]$表示在第$i$天,手上有$j$股时的最大收益 第一,我们可以直接买股票,即$dp[i][j]=-j*AP_i$,这个直接计算即可 ...

  9. 单调队列优化DP || [SCOI2010]股票交易 || BZOJ 1855 || Luogu P2569

    题面:P2569 [SCOI2010]股票交易 题解: F[i][j]表示前i天,目前手中有j股的最大收入Case 1:第i天是第一次购买股票F[i][j]=-j*AP[i]; (1<=j< ...

随机推荐

  1. NOI2007 项链工厂

    题目链接:戳我 60pts 有一点容易写错的小细节: 比如说求全局的段数的时候,如果只有一种颜色,那么当左右端点相等时,就不要ans--了. 注意右端点小于左端点的情况. #include<io ...

  2. 7.9模拟赛T1图的遍历(dfs)

    图的遍历(dfs) [题目描述] 对于一个有向图G来说,我们存在一个经典的遍历算法,就是DFS (深度优先搜索遍历).将G以1号点为起点进行DFS后,我们可以 得到G的一棵DFS遍历树T.就此,我们可 ...

  3. [CSP-S模拟测试]:Drink(模拟)

    题目传送门(内部题10) 输入格式 输入第一行三个数$N,M,Q$分别表示棋盘的行数.列数和操作个数.接下来$N$行每行$M$个数表示一开始棋盘上宝物的价值.接下来$Q$行每行$3$个数$x,y,c$ ...

  4. JDBC之——原理和连接数据库

    JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口 ...

  5. Nginx事件管理之epoll模块

    1. epoll 原理 假设有 100 万用户同时与一个进程保持着 TCP 连接,而每一时刻只有几十个或几百个 TCP 连接时活跃的(接收到 TCP 包),也就是说,在每一时刻,进程只需要处理这 10 ...

  6. LeetCode 199. 二叉树的右视图(Binary Tree Right Side View)

    题目描述 给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值. 示例: 输入: [1,2,3,null,5,null,4] 输出: [1, 3, 4] 解释: 1 ...

  7. Laravel5.2中Eloquent与DB类的区别是什么?

    要了解这些先看看关于数据库组件的那些事儿(就是 Eloquent ORM) 数据库组件大概分了三层: 数据库连接层 查询构造层 应用层 来看一下每一层有哪些东西,分别对应文档的哪一部分: 数据库连接层 ...

  8. java 获取本地 mac 地址

    主要参考:Java获取本机MAC地址/IP地址/主机名 做的更改: 1.我的windows是中文版,程序中获取mac时是按照physical address 获取的,添加上"物理地址&quo ...

  9. 如何查看 SELinux状态及关闭SELinux

    查看SELinux状态: 1./usr/sbin/sestatus -v      ##如果SELinux status参数为enabled即为开启状态 SELinux status:         ...

  10. ModuleNotFoundError: No module named '_sqlite3'的解决办法:pipenv的用法

    export PIPENV_VENV_IN_PROJECT=1pipenv --venvpipenv --where pipenv install -r requirements.txtpipenv ...