题目描述
今天qwb要参加一个数学考试,这套试卷一共有n道题,每道题qwb能获得的分数为ai,qwb并不打算把这些题全做完,
他想选总共2k道题来做,并且期望他能获得的分数尽可能的大,他准备选2个不连续的长度为k的区间,
即[L,L+1,L+2,....,L+k-1],[R,R+1,R+2,...,R+k-1](R >= L+k)。
题解
很明显题目就是让找两个长度为k,并且不相交的区间的最大和。
如果直接暴力枚举两个区间的端点来得到答案时间复杂度为O(n^2)显然不行
那么我们考虑怎样只枚举一个区间的端点,然后O(1)得到其他区间的最大值
显然是DP啦!!!
当我们枚举到i时,假设他所代表的的右区间为[i-k+1,i-k+2,....,i],
那么就只需要知道1 ~ i-k中最大的字段和就可以了。
这一部分可以O(n)预处理出来,所以每个位置i的答案就为 f[i-k]+sum[i]-sum[i-k](f[i]为1 ~ i构成一个区间的最大值,sum[i]为ai的前缀和 )。
然后所有i取最大值就可以啦
要注意ai可能小于0,所以要把F数组初始化为负无穷
以下变形由于没有找到相应题目,仅供参考~
变形
1.考虑只有一个区间,长度不限的求法(这是一个经典模型)。
设dp[i]为以i为结尾的最大值 ,所以 dp[i]=max(dp[i-1]+a[i],a[i]),求出dp数组后数组中最大的值就是答案
如果会了上面的话,求两个区间的话似乎也不是很难了。考虑到最后的两个区间是被隔开的,假设是被i,那么我们只需要求出i之前构成一个区间的最大值和i之后构成一个区间的最大值(这部分参考求一个区间的情况,只需正向dp一下取max,反向dp一下取maxjiu'ke),取和。那么被i隔开的情况就求出来了。
然后在枚举i就可以得到所有的情况。时间复杂度O(n)
2区间数目变多——找 {m}m个长度为 {k}k 的不相交区间使得他们的权值和最大 (1≤n≤5000)
还是dp,令dp[i][j]表示前i个数字构成j个不相交区间的最大值后dp[n][k]即为所求
转移方程:dp[i][j]=min{dp[i-k][j-1]+sum[i]-sum[i-k]} 复杂度O(n*k)
三:区间数目变多且不限制长度——找 {m}m 个不相交长度不限的区间使得他们权值和最大(1≤n≤5000)
这种情况,我想了几种方法都不是很优秀,所以一起期待其他大佬的题解吧!!
#include<bits/stdc++.h> #define pb push_back #define fi first #define se second using namespace std; typedef long long LL; typedef unsigned long long uLL; typedef pair<int,int> PII; typedef pair<LL,LL> PLL; typedef vector<int> VI; const int N = 5e3+5; const int mod =1e9+7; const LL inf = 0x3f3f3f3f3f3f3f3f; const int maxn = 1e6+6; int _,n,k; LL a[maxn],f[maxn]; int main(){ for(scanf("%d",&_);_;_--){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]+=a[i-1]; for(int i=1;i<k;i++) f[i]=-inf; for(int i=k;i<=n;i++) f[i]=max(f[i-1],a[i]-a[i-k]); LL ans=-inf; for(int i=2*k;i<=n;i++){ ans=max(ans,a[i]-a[i-k]+f[i-k]); } printf("%lld\n",ans); } }