一、题意
1.简述
东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有ai个人(1<=i<=n)。从第i到第j个宿舍一共有sum(i,j)=a[i]+…+a[j]个人
这让宿管阿姨非常开心,并且让东东扫楼m次,每一次数第i到第j个宿舍sum(i,j)
问题是要找到sum(i1, j1) + … + sum(im,jm)的最大值。且ix <= iy <=jx和ix <= jy <=jx的情况是不被允许的。也就是说m段都不能相交。
注:1 ≤ i ≤ n ≤ 1e6 , -32768 ≤ ai ≤ 32767 人数可以为负数。。。。(1<=n<=1000000)
2.输入格式
输入m,输入n。后面跟着输入n个ai 处理到 EOF(也即多组数据)
3.输出格式
输出最大和
4.样例
Input
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
Output
6
8
Hint
数据量很大,需要scanf读入和dp处理。
二、算法
主要思路
首先确定状态的定义:
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个元素中选择了
j
j
j个区间进行了扫楼,同时必须扫了第
i
i
i个元素,得到的最大的数值。
状态转移方程如下:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
i
−
1
]
[
j
]
+
a
[
i
]
,
f
[
k
]
[
j
−
1
]
+
a
[
i
]
}
j
−
1
≤
k
≤
i
−
1
f[i][j]=max\{f[i-1][j]+a[i],f[k][j-1]+a[i] \} \ \ \ \ \ \ \ j-1\le k\le i-1
f[i][j]=max{f[i−1][j]+a[i],f[k][j−1]+a[i]} j−1≤k≤i−1
f
[
i
−
1
]
[
j
]
+
a
[
i
]
f[i-1][j]+a[i]
f[i−1][j]+a[i]表示在取第i-1个数的基础上,在最后一个区间上加上第
i
i
i个数,所以并没有增加新的区间,区间个数还是
j
j
j。
f
[
k
]
[
j
−
1
]
+
a
[
i
]
f[k][j-1]+a[i]
f[k][j−1]+a[i]表示已经有了
j
−
1
j-1
j−1个区间,第
i
i
i个数的加入产生了一个新的区间,而且并不能保证第
j
−
1
j-1
j−1个区间的最后一个元素一定跟元素
i
i
i挨着。所以根据这个思路,
k
k
k应该从
j
−
1
j-1
j−1到
i
−
1
i-1
i−1取值。
化简
时间复杂度不能降低,但是能够降低空间复杂度,将
2
2
2维数组降为
1
1
1维。
观察状态转移方程可以发现,假设外层每一层循环确定的是几个区间,也就是第
2
2
2维,那么实际上每一层的
f
f
f数组只需要本层前面的一个数据(
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j])和上一层的数据(
f
[
k
]
[
j
−
1
]
f[k][j-1]
f[k][j−1]),而由于我们求的是最大值,那我们可以另外设置一个最大值数组,记录从头(每一层可能的最小的元素)到第i个元素,哪个
f
[
k
]
f[k]
f[k]最大,这个也可以用一个
1
1
1维数组表示,随着一层的遍历,该数组得到更新,然后下一层会用到这个数组。这里的最大值数组用来代替状态方程中的
f
[
k
]
[
j
−
1
]
f[k][j-1]
f[k][j−1]部分。这样,我们在求
f
[
i
]
[
j
]
f[i][j]
f[i][j]时就能够只使用本层的
1
1
1个数据
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j],而且如果我们按照从左往右的顺序遍历一层,那么求
f
[
i
]
[
j
]
f[i][j]
f[i][j](化到一维也就是
f
[
i
]
f[i]
f[i])时
f
[
i
]
[
j
]
f[i][j]
f[i][j](化到一维也就是
f
[
i
−
1
]
f[i-1]
f[i−1])就已经准备好了。而
f
[
k
]
[
j
−
1
]
f[k][j-1]
f[k][j−1]直接利用上层更新好的最大值数据,这样也不用再去遍历一遍求最大值了,相当于降低了时间复杂度。
结果
求出最后一层,也就是含有m个区间的情况之后,这个时候并不是 f [ n ] [ m ] f[n][m] f[n][m]是最终答案,因为最终答案可能并不包含第 n n n个元素,所以应该从第 m m m个元素到第 n n n个元素遍历f数组,取最大值。
三、代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int maxn = 1000010;
int f[maxn];
int maxf[maxn];
int a[maxn];
int m,n;
int main(){
while(scanf("%d%d",&m,&n)!=EOF){
for(int i=0;i<=n;i++){
f[i] = 0;maxf[i] = 0;
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int j=1;j<=m;j++){
for(int i=j;i<=n;i++){
int ma = f[i-1] + a[i];
int mb = maxf[i-1] + a[i];
f[i] = ma>mb?ma:mb;
if(i==j||i==j+1){
maxf[i-1] = f[i-1];
}
else maxf[i-1] = maxf[i-2]>f[i-1]?maxf[i-2]:f[i-1];
}
}
int mmax = -1e9; //-maxn竟然不行,这是因为,如果所有的都是负数,那就很有可能会超过-1000010
for(int i=m;i<=n;i++){
mmax = mmax>f[i]?mmax:f[i];
}
printf("%d\n",mmax);
}
cout<<-maxn<<endl;;
return 0;
}
/*
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
*/