首页 > 其他分享 >【每日一题】分隔数组以得到最大和

【每日一题】分隔数组以得到最大和

时间:2023-04-19 11:57:28浏览次数:50  
标签:arr 分隔 int res 每日 maxV dfs 数组

1043. 分隔数组以得到最大和

关键词:动态规划、递归

题目来源:1043. 分隔数组以得到最大和 - 力扣(Leetcode)

题目描述

T动态规划
T递归

给你一个整数数组 arr,请你将该数组分隔为长度 最多 为 k 的一些(连续)子数组。分隔完成后,每个子数组的中的所有值都会变为该子数组中的最大值。

返回将数组分隔变换后能够得到的元素最大和。本题所用到的测试用例会确保答案是一个 32 位整数。

输入:arr = [1,15,7,9,2,5,10], k = 3
输出:84
输入:arr = [1,4,1,5,7,3,6,1,9,9,3], k = 4
输出:83
输入:arr = [1], k = 1
输出:1
数据范围
1 <= arr.length <= 500
0 <= arr[i] <= 109
1 <= k <= arr.length

问题分析

很容易发现,当确定第一个子数组后,剩下的便是一个与原问题相似的子问题,于是便得到如下朴素解法。

朴素递归

第一个子数组的起点已知,为i(当前问题的第一个子数组的起点为0),枚举第一个子数组的终点j,注意区间长度不能超过k,于是第一个子数组就为[i, j],也即有如下等式

dfs(i) = max( (i-j+1)*maxV + dfs(j+1) )

其中,dfs(i)表示第一个子数组起点为i时的最大和,dfs(0) 便是最终结果。

解空间可近似看做一棵高度为n的k叉树,所以时间复杂度为O(k^n),空间复杂度为O(n)。

记忆化

考虑到递归过程存在许多重复计算,因此可采用记忆化搜索,使用f[i]来记录dfs(i)的结果。

对于每个i,只有f[i]还没计算过时,才会分叉计算,一共分成k叉,于是时间复杂度为O(nk),空间复杂度为O(n)。

动态规划

至此,已经有动态规划的雏形了。递归的时候,i在“递”时是从小到大,“归”时是从大到小,于是,动态规划中的第一层for循环的i便与“归”保持一致,第二层for循环同样是枚举第一个子数组终点。

动态规划的时间复杂度为O(nk),空间复杂度为O(n)。

以上动态规划的思路是从递归转换而来,如果不借助递归,我们可以按如下来思考。

第一个子数组的起点已经确定,只需确定第一个子数组的终点就可完全确定第一个子数组。一旦第一个子数组的终点确定,第二个子数组的起点就确定,只需确定第二个子数组的终点就可以完全确定第二个子数组。依次类推。

由于最后一个子数组的终点也是确定的,于是,可以从前往后枚举每一个起点i,f[i]表示第一个数组起点为i时的最大和,于是f[0]便是题目所求答案。

空间优化

由于本题的n不超过500,所以此步并不是很重要,但这种思路需要注意。

在动态规划中,计算f[i]时,只会用到f[i..i+k+1]共k+1个位置,在计算f[i-1]时,只会用到f[i-1..i+k]共k+1个位置,可以发现,每次用到的位置会后移,于是可采用一个滚动数组来优化空间,用f[i%k]来代替法f[i],但要注意,i与i+k+1是同余的。空间复杂度为O(k)。

代码实现

朴素递归

int maxSumAfterPartitioning(vector<int> &arr, int k) {
    int n = arr.size();
    function<int(int)> dfs = [&](int i) {
        int res = 0;
        // 枚举当前问题的第一个子数组的终点
        for (int j = i, maxV = 0; j < i + k && j < n; j++) {
            maxV = max(arr[j], maxV);
            res = max(dfs(j + 1) + (j - i + 1) * maxV, res);
        }
        return res;
    };
    return dfs(0);
}

记忆化

int maxSumAfterPartitioning(vector<int> &arr, int k) {
    int n = arr.size(), f[n];
    memset(f, -1, sizeof(f));
    function<int(int)> dfs = [&](int i) {
        if (i >= n)return 0;
        int &res = f[i];
        if (res != -1)return res;
        // 枚举当前问题的第一个子数组的终点
        for (int j = i, maxV = 0; j < i + k && j < n; j++) {
            maxV = max(arr[j], maxV);
            res = max(dfs(j + 1) + (j - i + 1) * maxV, res);
        }
        return res;
    };
    return dfs(0);
}

动态规划

int maxSumAfterPartitioning(vector<int> &arr, int k) {
    int n = arr.size(), f[n + 1];
    memset(f, 0, sizeof f);
    for (int i = n - 1; i >= 0; i--) {
        // 枚举当前问题的第一个子数组的终点
        for (int j = i, maxV = 0; j < i + k && j < n; j++) {
            maxV = max(arr[j], maxV);
            f[i] = max(f[j + 1] + (j - i + 1) * maxV, f[i]);
        }
    }
    return f[0];
}

空间优化

int maxSumAfterPartitioning(vector<int> &arr, int k) {
    int n = arr.size(), f[k];
    memset(f, 0, sizeof f);
    for (int i = n - 1; i >= 0; i--) {
        // 由于i和i+k+1同余k,所以在计算过程中不能直接覆盖
        int res = 0;
        // 枚举当前问题的第一个子数组的终点
        for (int j = i, maxV = 0; j < i + k && j < n; j++) {
            maxV = max(arr[j], maxV);
            res = max(f[(j + 1) % k] + (j - i + 1) * maxV, res);
        }
        f[i % k] = res;
    }
    return f[0];
}

标签:arr,分隔,int,res,每日,maxV,dfs,数组
From: https://www.cnblogs.com/zijie1024/p/17332822.html

相关文章

  • 每日八股文之Java
    1、请你说说死锁定义及发生的条件得分点:争夺共享资源、相互等待、互斥条件、请求和保持条件、不剥夺条件、环路等待条件死锁定义:两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或......
  • python多进程-多元数组
    多进程分配数组任务,并原地修改frommultiprocessingimportPool,Manager,cpu_countimportnumpyasnpimporttimedeffunc(i,j):#wait100stime.sleep(0.5)returni+jif__name__=='__main__':mat=np.zeros((10,10)).tolist()po......
  • C语言 正确理解二维数组首地址
    在一维数组中,数组名表示的是数组第一个元素的地址inta[10],*p=a;那么二维数组呢inta[3][4],a表示的是元素a[0][0]的地址吗?不是!二维数组就是一维数组,二维数组a[3][4]就是有三个元素a[0]、a[1]、a[2]的一维数组,所以数组a的第一个元素不是a[0][0],而是a[0],所以数组名......
  • 每日编程一小时(第十天)
    一.问题描述5本新书借给3人,没人最多借一本,有多少种借法二.设计思路1.采用枚举的方法列出所有的选择情况2.利用判定条件删去不符合条件的情况,剩下的全部为符合条件的情况三.流程图 四.代码实现#include<iostream>usingnamespacestd;intmain(){intA,B,C,fl......
  • 2023/4/18每日随笔
       今天,上了英语口语,数据库,和python,数据库课上学了需求分析,数据库的建立等等,是一些以后做项目的要用到的东西。然后,python课上写报告,然后跑了八圈,晚上写了项目,解决了Androidfragment的添加bug,以及数据传输问题,我写的很乱,我觉得应该有一个东西可以在整个项目共享,但是我不知道......
  • 关于大数乘法的数组类型问题(int 还是char)
    可以知道在处理高精度乘法的时候,我们是不考虑当场进位的,在所有位数都模拟完竖式乘法后才进行逐位进位,这就要求存储每个位的数组保证不会爆掉溢出众所周知char类型最多只能存储到255,非常非常容易溢出成负数,对于char型数组要考虑每一步乘法都要进位。而int型数组最大21亿就不用考......
  • 每日八股文之Java
    1、请你说说多线程得分点:线程和进程的关系、为什么使用多线程进程是操作系统资源调度的基本单位,线程是处理器任务调度和执行的基本单位,一个进程可以创建多个线程,每个线程有自己独立的程序计数器,本地方法栈和虚拟机栈,线程之间共享进程的堆和方法区。线程之间是通过时间片算法......
  • 每日团队小结
    昨天对opencv,ocr学习和使用了今天对程序的编写,servlet编写,以及对接口调用部署的浏览器找不到自己写的servlet ......
  • #yyds干货盘点# LeetCode面试题:删除有序数组中的重复项 II
    1.简述:给你一个有序数组nums,请你原地删除重复出现的元素,使得出现次数超过两次的元素只出现两次,返回删除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(1)额外空间的条件下完成。 说明:为什么返回数值是整数,但输出的答案是数组呢?请注意,输入数......
  • 每日记录
    今天研究了javaweb的记住用户,就是用户登陆过之后可以选择记住用户,下次登录时不用再输入密码账号具体实现如下importwmx.bean.User;importwmx.service.UserService;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.s......