题目链接:选择数字 - 洛谷
解题报告:
思路1:
参考代码:
#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一样)
#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:(正难则反,本质还是单调队列)