当前位置:   article > 正文

【洛谷 P2034】选择数字(单调队列优化dp)_洛谷 单调队列

洛谷 单调队列

 题目链接:选择数字 - 洛谷

解题报告:

思路1:

 参考代码:

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<deque>
  4. using namespace std;
  5. const long long Maxn=100000+20,inf=0x3f3f3f3f;
  6. long long a[Maxn],s[Maxn],f[Maxn][2];
  7. deque <long long> q;
  8. long long n,k;
  9. inline long long read()
  10. {
  11. long long s=0,w=1;
  12. char ch=getchar();
  13. while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
  14. while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
  15. return s*w;
  16. }
  17. void pop(long long x)
  18. {
  19. while(1)
  20. {
  21. if(q.empty())break;
  22. if(q.front()>=x-k)break;
  23. q.pop_front();
  24. }
  25. }
  26. void push(long long x)
  27. {
  28. while(1)
  29. {
  30. if(q.empty())break;
  31. long long i=q.back();
  32. if(f[i][0]-s[i]>=f[x][0]-s[x] && i!=x)break;
  33. q.pop_back();
  34. }
  35. q.push_back(x);
  36. }
  37. int main()
  38. {
  39. // freopen("in.txt","r",stdin);
  40. n=read(),k=read();
  41. for(long long i=1;i<=n;++i)
  42. a[i]=read(),s[i]=s[i-1]+a[i];
  43. for(long long i=1;i<=n;++i)
  44. {
  45. pop(i);
  46. push(i-1);
  47. long long j=q.front();
  48. f[i][1]=s[i]+f[j][0]-s[j];
  49. f[i][0]=max(f[i-1][1],f[i-1][0]);
  50. }
  51. printf("%lld\n",max(f[n][0],f[n][1]));
  52. return 0;
  53. }

 思路2:(和思路1一样)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. LL n,k,sum[100001],dp[100001];
  5. int main(){
  6. cin>>n>>k;
  7. LL maxn=-INF,pos,ns;
  8. for(int x,i=1;i<=n;++i) cin>>x,sum[i]=sum[i-1]+x;
  9. for(int i=1;i<=k;++i){//前k个是可以直接选的(n==k时有效)
  10. dp[i]=sum[i];
  11. if((ns=dp[i-1]-sum[i])>=maxn) maxn=ns,pos=i;
  12. }
  13. for(int i=k+1;i<=n;++i){
  14. if(pos<i-k){//当过期时
  15. maxn=-INF;//注意所有数组都要是long long的
  16. for(int j=pos+1;j<=i;++j){//汇编代码友好
  17. if((ns=dp[j-1]-sum[j])>=maxn){
  18. maxn=ns,pos=j;
  19. }
  20. }
  21. } else {
  22. if((ns=dp[i-1]-sum[i])>=maxn) maxn=ns,pos=i;//寄存器友好(不开O2)
  23. }
  24. dp[i]=dp[pos-1]+sum[i]-sum[pos];
  25. }
  26. printf("%lld",dp[n]);
  27. return 0;
  28. }

思路3:(最短路)

因为本题需要在每隔最多k个数字就要放弃一个,所以对于每一个数字i,向它后面的i+1~i+k+1连边,之后设一个超级起点s连向1~k,再将n-k+1~n的节点连向一个超级终点t,之后以s为起点跑最短路,所有阶段权值的总和sum减去s到t的距离就是最后的答案。

由于需要连得边数是O(nk)级别的,并且是一个点向一个区间的点连一条边,因此可以用线段树优化建图,讲连的边数变成O(nlogk),总的时间复杂度是O(nlognlogn)

思路4:(正难则反,本质还是单调队列)

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号