WEEK12 周记 作业——动态规划_必做题3

一、题意

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[i1][j]+a[i],f[k][j1]+a[i]}       j1ki1 f [ i − 1 ] [ j ] + a [ i ] f[i-1][j]+a[i] f[i1][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][j1]+a[i]表示已经有了 j − 1 j-1 j1个区间,第 i i i个数的加入产生了一个新的区间,而且并不能保证第 j − 1 j-1 j1个区间的最后一个元素一定跟元素 i i i挨着。所以根据这个思路, k k k应该从 j − 1 j-1 j1 i − 1 i-1 i1取值。


化简

时间复杂度不能降低,但是能够降低空间复杂度,将 2 2 2维数组降为 1 1 1维。
观察状态转移方程可以发现,假设外层每一层循环确定的是几个区间,也就是第 2 2 2维,那么实际上每一层的 f f f数组只需要本层前面的一个数据( f [ i − 1 ] [ j ] f[i-1][j] f[i1][j])和上一层的数据( f [ k ] [ j − 1 ] f[k][j-1] f[k][j1]),而由于我们求的是最大值,那我们可以另外设置一个最大值数组,记录从头(每一层可能的最小的元素)到第i个元素,哪个 f [ k ] f[k] f[k]最大,这个也可以用一个 1 1 1维数组表示,随着一层的遍历,该数组得到更新,然后下一层会用到这个数组。这里的最大值数组用来代替状态方程中的 f [ k ] [ j − 1 ] f[k][j-1] f[k][j1]部分。这样,我们在求 f [ i ] [ j ] f[i][j] f[i][j]时就能够只使用本层的 1 1 1个数据 f [ i − 1 ] [ j ] f[i-1][j] f[i1][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[i1])就已经准备好了。而 f [ k ] [ j − 1 ] f[k][j-1] f[k][j1]直接利用上层更新好的最大值数据,这样也不用再去遍历一遍求最大值了,相当于降低了时间复杂度。


结果

求出最后一层,也就是含有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
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值