前面做过好几个最大连续子列的问题,这个题是找两个已知长度的子列,满足这两段的和最大这一条件。核心是动规以及树状数组。
先说一下树状数组:第一个是sum[i]=sum[i-1]+num[i];以及seq[i]=sum[i+k-1]-sum[i-1];seq[i]表示i到i+k-1的和。
动规:dp[i]=a表示在以第i到n-k+1为起点的长度为k的子列中以a为起点的最大。状态转移方程是 若seq[i]>seq[dp[i+1]],dp[i]=i;否则dp[i]=dp[i+1]
还要注意数据范围,要用long long 存
#include<cstdio>
#include<iostream>
#include<cstring>
#define LL long long
#define MAXN 2*100000+10
using namespace std;
LL n,k,num[MAXN],sum[MAXN],seq[MAXN];
int dp[MAXN];
int main()
{
// freopen("in.txt","r",stdin);
cin>>n>>k;
for(int i=1; i<=n; i++)
{
cin>>num[i];
sum[i]=sum[i-1]+num[i];
}
for(int i=1; i+k-1<=n; i++)
seq[i]=sum[i+k-1]-sum[i-1];
dp[n-k+1]=n-k+1;
for(int i=n-k; i>=0; i--)
{
if(seq[i]>=seq[dp[i+1]])
dp[i]=i;
else dp[i]=dp[i+1];
}
LL max=0;
int ans;
for(int i=1; i+k<=n; i++)
{
LL s=seq[i]+seq[dp[i+k]];
if(s>max)
{
max=s;
ans=i;
}
}
cout<<ans<<" "<<dp[ans+k]<<endl;
return 0;
}
第二种代码是合并了第一种代码的最后两个for循环,i是从n-k循环到1的,而每一个seq[temp]都是在当前i值下:起点>=i+k的和最大的子列。
#include<cstdio>
#include<iostream>
#include<cstring>
#define LL long long
#define MAXN 2*100000+10
using namespace std;
LL n,k,num[MAXN],sum[MAXN],seq[MAXN];
int dp[MAXN];
int main()
{
//freopen("in.txt","r",stdin);
cin>>n>>k;
for(int i=1; i<=n; i++)
{
cin>>num[i];
sum[i]=sum[i-1]+num[i];
}
for(int i=1; i+k-1<=n; i++)
seq[i]=sum[i+k-1]-sum[i-1];
int temp=n;
LL max=0;
int s,e;
for(int i=n-k; i>=0; i--)
{
if(seq[i+k]>=seq[temp])
temp=i+k;
if(seq[i]+seq[temp]>=max)
{
max=seq[i]+seq[temp];
s=i;
e=temp;
}
}
cout<<s<<" "<<e<<endl;
return 0;
}