单调队列优化dp
首先你得知道单调队列是个啥!!!!!!!!!!!!!!
简单来说
就是一个单调的队列(像是在放屁)
记住,千万不要用queue,难死你,就用一个数组,一个头指针一个尾指针
从尾指针添加元素(当然也可以删除)从前面提取最优值
用术语来说就是维护一个单调队列;(明白了吧???)
自己去网上搜吧
接下来进入正题了::::用这个鬼东西来优化dp
学了这么长时间dp了
你也知道dp的涉及面有多么广了,绝对不会让你只想出一个转移方程就AC;
这时候就要用到各种优化了;
先拿一个题;
F. 最大子序和
题目描述
输入格式
输出格式
样例
作为一个老dp人了
你不会连转移方程都想不出来吧;
我劝你重修dp吧
既然是求子序和,还是连续的!?(那必是前缀和走起啊)
设num[i]为这个序列,sum[i]为前i个的和,dp[i]为以a[i]为最后一个数的最大子序列;
好吧我承认我之前说错了,这个题的转移方程好像并不存在。。。。。
(姑且称它为一个dp题吧)
dp[i]=sum[i]-sum[j](i>j && i-j<=m)
那么我们就去维护一个单调队列,在sum[i]这个数组的基础上,使这个单调队列递增;
然后,贴上代码
#include<bits/stdc++.h> using namespace std; int n,m,num[300005]; int sum[300005]; int dp[300005]; int q[300005]; int ans=0x8fffffff; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&num[i]); sum[i]=sum[i-1]+num[i]; } int l=1,r=0; q[1]=1; for(int i=1;i<=n;i++){ dp[i]=sum[i]-sum[i-1]; while(l<=r && q[l]<i-m) l++; if(l<=r) dp[i]=sum[i]-sum[q[l]]; ans=max(ans,dp[i]); while(l<=r && sum[i]<sum[q[r]]) r--; q[++r]=i; } printf("%d",ans); }
那么,从现在开始
恭喜你正式踏入单调队列优化dp的大门;
好了接着讲下一道题目;;;;
B. 股票交易
题目描述
输入格式
输出格式
样例
数据范围与提示
#include<bits/stdc++.h> using namespace std; int t,maxp,w; int ap[2005],bp[2005],as[2005],bs[2005]; int dp[2005][2005]; int main() { memset(dp,0x8f,sizeof(dp)); scanf("%d%d%d",&t,&maxp,&w); for(int i=1;i<=t;i++){ scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]); } for(int i=1;i<=t;i++){ for(int j=0;j<=as[i];j++) dp[i][j]=-j*ap[i]; for(int j=0;j<=maxp;j++) dp[i][j]=max(dp[i][j],dp[i-1][j]); if(i<=w+1) continue; int l=1,r=0; int q[2005]={}; int u=i-w-1; for(int j=0;j<=maxp;j++){ while(l<=r && dp[u][j]+j*ap[i]>=dp[u][q[r]]+q[r]*ap[i]) r--; while(l<=r && q[l]<j-as[i]) l++; q[++r]=j; dp[i][j]=max(dp[u][q[l]]+q[l]*ap[i]-j*ap[i],dp[i][j]); } l=1;r=0; for(int j=maxp;j>=0;j--){ while(l<=r && dp[u][j]+j*bp[i]>=dp[u][q[r]]+q[r]*bp[i]) r--; while(l<=r && q[l]>j+bs[i]) l++; q[++r]=j; dp[i][j]=max(dp[u][q[l]]+q[l]*bp[i]-j*bp[i],dp[i][j]); } } int ans=0x8fffffff; for(int i=0;i<=maxp;i++){ ans=max(ans,dp[t][i]); //printf("%d\n",dp[t][i]); } printf("%d",ans); }
剩下的你们自己搞吧
还有个比较重要的东西
记住你找完转移方程之后
化简一下
比如股票交易的方程(i天数,j现在拥有的股票数)
dp[i][j]=max(dp[i-w-1]-k*ap[i]);
dp[i][j]=max(dp[i-w-1]+k*bp[i]);
只看这两个方程自然是啥也看不出来
你要找到不变量
k是你将要买或者卖出去的数量
这样想 k=maxp-ki;
那么我们完全可以先加或者减去maxp*ap[i](bp[i]);
这样就变成了dp[i][j]=max(dp[i-w-1]+maxp*ap[i]-ki*ap[i]) 卖出去同理
再将ki加回来(减)
注意卖股票的循环是倒序的
就像背包一样
防止紊乱
单调队列优化的dp基本都是这样
先加上总的,然后再一点点减,这样就实现了单调性
恩的~~~~~~