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

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

解题报告:

思路1:

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

 参考代码:

#include<cstdio>
#include<iostream>
#include<deque>
using namespace std;
const long long Maxn=100000+20,inf=0x3f3f3f3f;
long long a[Maxn],s[Maxn],f[Maxn][2];
deque <long long> q;
long long n,k;
inline long long read()
{
long long s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
void pop(long long x)
{
while(1)
{
if(q.empty())break;
if(q.front()>=x-k)break;
q.pop_front();
}
}
void push(long long x)
{
while(1)
{
if(q.empty())break;
long long i=q.back();
if(f[i][0]-s[i]>=f[x][0]-s[x] && i!=x)break;
q.pop_back();
}
q.push_back(x);
}
int main()
{
// freopen("in.txt","r",stdin);
n=read(),k=read();
for(long long i=1;i<=n;++i)
a[i]=read(),s[i]=s[i-1]+a[i];

for(long long i=1;i<=n;++i)
{
pop(i);
push(i-1);
long long j=q.front();
f[i][1]=s[i]+f[j][0]-s[j];
f[i][0]=max(f[i-1][1],f[i-1][0]);
}
printf("%lld\n",max(f[n][0],f[n][1]));

return 0;
}

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

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

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,k,sum[100001],dp[100001];

int main(){
cin>>n>>k;
LL maxn=-INF,pos,ns;
for(int x,i=1;i<=n;++i) cin>>x,sum[i]=sum[i-1]+x;
for(int i=1;i<=k;++i){//前k个是可以直接选的(n==k时有效)
dp[i]=sum[i];
if((ns=dp[i-1]-sum[i])>=maxn) maxn=ns,pos=i;
}
for(int i=k+1;i<=n;++i){
if(pos<i-k){//当过期时
maxn=-INF;//注意所有数组都要是long long的
for(int j=pos+1;j<=i;++j){//汇编代码友好
if((ns=dp[j-1]-sum[j])>=maxn){
maxn=ns,pos=j;
}
}
} else {
if((ns=dp[i-1]-sum[i])>=maxn) maxn=ns,pos=i;//寄存器友好(不开O2)
}
dp[i]=dp[pos-1]+sum[i]-sum[pos];
}
printf("%lld",dp[n]);
return 0;
}

思路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:(正难则反,本质还是单调队列)

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