标签:P2034 洛谷 队列 题解 方程 不取 单调 100010 DP
萌新刚学单调队列优化 DP ,发一篇题解纪念一下qwq
自认为详细的讲解
看到题目,是一个纯的选数问题,所以考虑用 DP 解。学过 DP 的都知道 DP 有三要素,所以我们先把这三个填完。
- 阶段,这个其实一般来说就是循环的东西,比较好填,看题目可知是每个数的位置(编号)
- 决策,对于每个数,我们当然有两种决策,即取和不取。
接下来是比较难的(虽然这题简单)状态(状态转移方程)了,这个需要根据决策来填,可以容易的写下下面的方程:
设\(f_{i,0}\)表示不取第\(i\)个数得到的最大结果,则有方程\(f_{i,0}=\max(f_{i-1,0},f_{i-1,1})\)
设\(f_{i,1}\)表示不取第\(i\)个数得到的最大结果,则有方程\(f_{i,1}=\max(f_{x,0}-s_x)+s_i\)(前提是\(i-k\leq x < i\))
然而时间复杂度是\(O(n^2)\),会 T ,所以我们要进行优化。
发现\(f_{x,0}-s_x\)随着的\(x\)的变化而变化,和\(i\)无关。并且因为他们是递增的,所以我们用\(f_{x,0}-s_x\)构造单调队列,把时间复杂度降到\(O(n)\)。
接下来就简单了,而且这一题也不用像模板写两头维护,只需要写一边就行,所以在循环内写上方程,敲一个单调队列外加上面的限制条件就行了。
AC code:
#include<bits/stdc++.h>
using namespace std;
long long n,m,l,r,x,s[100010],f[100010][10],q[100010];//这里要注意开longlong
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%lld",&x),s[i]=s[i-1]+x;//求前缀和
l=r=1;
for(int i=1;i<=n;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]);//方程
while(q[l]<i-m&&l<=r) l++;//维护队首
f[i][1]=f[q[l]][0]-s[q[l]]+s[i];//方程
while(f[i][0]-s[i]>f[q[r]][0]-s[q[r]]) r--;//维护队尾
r++;q[r]=i;
}
cout<<max(f[n][0],f[n][1]); //最后不要忘了再比较
return 0;
}
码字不易,给个赞呗qwq~~
标签:P2034,洛谷,队列,题解,方程,不取,单调,100010,DP
来源: https://www.cnblogs.com/Akn-Wind/p/solution-luoguP2034.html
本站声明:
1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。