标签:tail continue main get return 设计 src cstring 一个
https://darkbzoj.tk/problem/1855
https://www.luogu.com.cn/problem/P2569
单调队列优化,还是看了一眼题解才做出来的/kk
\(1\leq BP_i\leq AP_i\leq 1000,1\leq AS_i,BS_i\leq\text{MaxP}1\)
\(0\leq W<T\leq 2000,1\leq\text{MaxP}\leq20000\)
可以想到设计状态 \(f_{i,j}\) 表示前 \(i\) 天,有 \(j\) 个股票,的最大获利
分成这么几种情况来进行转移:
显然,第三种情况不成立,因为没买过不能卖
分别讨论其它情况
第一种,\(f_{i,j}=f_{i-1,j}\)
第二种,显然 \(f_{i,j}=-AP_i\cdot j\)
三四种稍微复杂
肯定是从第 \(i-w+1\) 天转移而来,因为 \(i-w\sim i-1\) 都不符合限制,不行
但为什么不考虑 \(i-w+1\) 之前的?因为之前某一天对应的状态会通过第一种不买不卖的情况一路转移到第 \(i-w+1\) 天
看题解前就是因为这一点没想过来才卡着没做出来的
第三种的转移方程:
不难理解,\(k\) 的限制条件是为了符合买入数量的显示,\(-(j-k)\cdot AP_i\) 就是买入要花的钱
同理,第四种也类似:
但发现这样转移复杂度不行,还要优化一下
因为是取 \(\max\),而且还是一个不断整体移动且长度不变的区间(比如第三种情况是 \(j-AS_i\sim j-1\)),所以想到单调队列,其实是用标签搜进来的
给式子变形,以第三种为例
就是用一下乘法分配然后把 \(-j\cdot AP_i\) 提出去
这样,每次把 \(f_{i-w-1,k}+k\cdot AP_i\) 放入队列,然后更新的时候就是用队列的最大值减去 \(-j\cdot AP_i\) 来取 \(\max\) 更新
第四种的式子:
代码:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<‘0‘||c>‘9‘){if(c==‘-‘) y=0;c=std::getchar();}
while(c>=‘0‘&&c<=‘9‘){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
int n,maxp,w;
int f[2005][2005];
int tail,head,num[2005],ind[2005];
int main(){
n=read();maxp=read();w=read();
std::memset(f,128,sizeof f);
for(reg int ap,bp,as,bs,i=1;i<=n;i++){
ap=read();bp=read();as=read();bs=read();
for(reg int j=0;j<=as;j++) f[i][j]=-ap*j;//直接买
for(reg int j=0;j<=maxp;j++) f[i][j]=std::max(f[i][j],f[i-1][j]);//不买不卖
if(i<=w) continue;
//从原来某状态继续买
tail=head=0;num[0]=f[i-w-1][0];ind[0]=0;
for(reg int tmp,j=1;j<=maxp;j++){
if(tail<=head) f[i][j]=std::max(f[i][j],num[tail]-j*ap);
tmp=f[i-w-1][j]+j*ap;
while(tail<=head&&num[head]<=tmp) head--;
num[++head]=tmp;ind[head]=j;
while(tail<=head&&ind[tail]<j+1-as) tail++;
}
//从原来的某状态继续卖
//因为这个被转的区间是 [j+1,j+bs],所以倒叙循环
tail=head=0;num[0]=f[i-w-1][maxp]+bp*maxp;ind[0]=maxp;
for(reg int tmp,j=maxp-1;~j;j--){
if(tail<=head) f[i][j]=std::max(f[i][j],num[tail]-j*bp);
tmp=f[i-w-1][j]+j*bp;
while(tail<=head&&num[head]<=tmp) head--;
num[++head]=tmp;ind[head]=j;
while(tail<=head&&ind[tail]>j-1+bs) tail++;
}
}
int ans=0;
for(reg int i=0;i<=maxp;i++) ans=std::max(ans,f[n][i]);
std::printf("%d",ans);
return 0;
}
标签:tail continue main get return 设计 src cstring 一个
原文地址:https://www.cnblogs.com/suxxsfe/p/13122006.html