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

本文探讨了一种针对扫楼任务的最优化算法,旨在通过动态规划策略寻找多个不相交区间的最大值和,解决宿管阿姨分配的扫楼任务问题。算法详细介绍了状态定义、状态转移方程及空间复杂度优化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、题意

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]表示前iii个元素中选择了jjj个区间进行了扫楼,同时必须扫了第iii个元素,得到的最大的数值。
状态转移方程如下:f[i][j]=max{f[i−1][j]+a[i],f[k][j−1]+a[i]}       j−1≤k≤i−1f[i][j]=max\{f[i-1][j]+a[i],f[k][j-1]+a[i] \} \ \ \ \ \ \ \ j-1\le k\le i-1f[i][j]=max{f[i1][j]+a[i],f[k][j1]+a[i]}       j1ki1f[i−1][j]+a[i]f[i-1][j]+a[i]f[i1][j]+a[i]表示在取第i-1个数的基础上,在最后一个区间上加上第iii个数,所以并没有增加新的区间,区间个数还是jjjf[k][j−1]+a[i]f[k][j-1]+a[i]f[k][j1]+a[i]表示已经有了j−1j-1j1个区间,第iii个数的加入产生了一个新的区间,而且并不能保证第j−1j-1j1个区间的最后一个元素一定跟元素iii挨着。所以根据这个思路,kkk应该从j−1j-1j1i−1i-1i1取值。


化简

时间复杂度不能降低,但是能够降低空间复杂度,将222维数组降为111维。
观察状态转移方程可以发现,假设外层每一层循环确定的是几个区间,也就是第222维,那么实际上每一层的fff数组只需要本层前面的一个数据(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]最大,这个也可以用一个111维数组表示,随着一层的遍历,该数组得到更新,然后下一层会用到这个数组。这里的最大值数组用来代替状态方程中的f[k][j−1]f[k][j-1]f[k][j1]部分。这样,我们在求f[i][j]f[i][j]f[i][j]时就能够只使用本层的111个数据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]是最终答案,因为最终答案可能并不包含第nnn个元素,所以应该从第mmm个元素到第nnn个元素遍历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
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值