题目大意
从数组中选出$k$个长度为$m$的不相邻区间,求$k$个区间求和的最大值。
思路
序列求和不难联想到前缀和。呢么我们可以预处理出来所以可选的区间,然后做dp。
状态转移
$f[i][j]$表现从前$i$个区间里选,恰好选了$j$个区间的最大值。
呢么就可以分为第$i$个区间选不选。
$if(i-m<0)$ $f[i][j]=max(f[i-1][j],w[i])$;
$else$ $f[i][j]=max(f[i-1][j],f[i-m][j-1]+w[i])$;
至于为什么是$i-m$,是因为选了这个区间,由于题目限制,它前面的$m-1$个区间就不能选了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5010;
ll n,m,k;
ll a[N],b[N];
ll w[N];
ll f[N][N];
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=b[i-1]+a[i];
}
ll cnt=0;
for(int i=1;i+m-1<=n;i++)
{
w[++cnt]=b[i+m-1]-b[i-1];//预处理区间
}
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=k;j++)
{
if(i-m<0) f[i][j]=max(f[i-1][j],w[i]);//此时最多选择一个区间
else f[i][j]=max(f[i-1][j],f[i-m][j-1]+w[i]);
}
}
cout<<f[cnt][k];
return 0;
}
这里是不是可以写成<=,我改后也对了
是的,一样的,0的时候没有f[0][j]就是0
好的
真的强
赞,这个思路更和我做题的思路一样,可惜我能力不够没想出来
这个叼,转化成01背包。
碉堡了
为什么
i<m
时f[i-1][j]
要和w[i]
取最大值呢此时的话,$i-m<0$,为负数,说明此时最多只能选择一个区间,有两种选择,不选第$i$个区间,就是$f[i-1][j]$,选第$i$个区间(前面的区间一定不能选了),呢么就是$w[i]$
比赛的时候就是这句没写出来。。。。
我比赛的时候调了半天没调出来。。。
比y总那个好理解欸!
自己不是多-1,就是少-1
请问为什么i可以直接减m,i表示的是区间数,m表示的是区间长度。要是i表示右端点的话还能理解
因为枚举可选区间的时候是连续有相交的区间,所以当前选择编号为
i
的区间时,区间编号大于i-m
的都不能选,因为和i
相交了,刚画样例才想明白答主在这里进行了前缀和操作,将i进行了操作,变成了整个区间的长度
明白了,原来是连续相交的区间的意思,谢谢!
6
### 秒啊