连续子数组的最大和(从暴力理解到DP)

news2025/1/12 3:44:04

连续子数组的最大和

    • 题目
    • 思路
      • 暴力解题思路
      • 画出矩阵进行分析
      • 确定转移方程
      • DP代码

题目

思路

从leetcode上看到的题解,突然恍然大悟,之前不容易理解转移方程终于理解了,这个思路真的对新手很友好,现在出一个C++版本,而且,我认为不应该仅仅知道最大的和多少,而且还要知道是哪个子串!

暴力解题思路

暴力的思路应该很好想,直接遍历得出所有可能的子串和,然后比较大小即可。

// O(n^2) 的暴力解法
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main()
{                
    int nums[1000];
    int n;
    cin>>n;
    for(int i=0; i<n; i++){
        cin>>nums[i];
    }                
    int mmax = INT_MIN;
    for(int i=0; i<n; i++){
        int sum = 0;
        for(int j=i; j<n; j++){
            sum += nums[j];
            mmax = max(mmax,sum);
        }
    }  
    printf("最大子数列的和为:%d",mmax);          
    return 0;                   
}

上面的代码很明显,一定超时,原因在于,我们具有重复计算的数据,具体可以列出几个相关的子序列进行观察即可.

画出矩阵进行分析

在求解过程中需要计算的子数组一定如下所列,其中 s u m ( i , j ) sum(i,j) sum(i,j)代表计算从 n u m s [ i ] nums[i] nums[i] n u m s [ j ] nums[j] nums[j]的元素之和,我们要找到最大的 s u m ( i , j ) sum(i,j) sum(i,j)

sum(0,0)
sum(0,1)sum(1,1)
sum(0,2)sum(1,2)sum(2,2)
sum(0,3)sum(1,3)sum(2,3)sum(3,3)
································

从上面的表格我们可以看出,比如在第四行,我们在计算从 0 − 3 0-3 03的的范围内,哪个子数列的和最大时,比较过程如下:

  • s u m ( 0 , 3 ) = n u m s [ 3 ] + n u m s [ 2 ] + n u m s [ 1 ] + n u m s [ 0 ] + 0 sum(0,3) = nums[3] + nums[2] + nums[1] + nums[0]+0 sum(0,3)=nums[3]+nums[2]+nums[1]+nums[0]+0
  • s u m ( 1 , 3 ) = n u m s [ 3 ] + n u m s [ 2 ] + n u m s [ 1 ] + 0 sum(1,3) = nums[3] + nums[2] + nums[1]+0 sum(1,3)=nums[3]+nums[2]+nums[1]+0
  • s u m ( 2 , 3 ) = n u m s [ 3 ] + n u m s [ 2 ] + 0 sum(2,3) = nums[3] + nums[2]+0 sum(2,3)=nums[3]+nums[2]+0
  • s u m ( 3 , 3 ) = n u m s [ 3 ] + 0 sum(3,3) = nums[3] + 0 sum(3,3)=nums[3]+0

从上面的过程可以看出,每个都要加上一个 n u m s [ 3 ] nums[3] nums[3],所以,可以直接将它先不看,直接观察剩下的式子, 不正是求从 [ 0 − 2 ] [0-2] [02]的范围内的子数列最大的和嘛?

所以,我们只要每次用dp[i] 记录下来这个上一次的子数列的最大的和,不就可以在下一次计算时,就可以省略这个步骤了?

加入dp[i]之后

💕
sum(0,0)dp[0]
sum(0,1)sum(1,1)dp[1]
sum(0,2)sum(1,2)sum(2,2)dp[2]
sum(0,3)sum(1,3)sum(2,3)sum(3,3)dp[3]
································dp[i]

所以加入dp之后,再计算从0-3的最大子数列的和就可以转换为:

0-3的最大子数列的和为: n u m s [ 3 ] + d p [ 2 ] nums[3] + dp[2] nums[3]+dp[2] (当dp[2]为正数时)

  • s u m ( 0 , 3 ) = n u m s [ 3 ] + n u m s [ 2 ] + n u m s [ 1 ] + n u m s [ 0 ] + 0 sum(0,3) = nums[3] + nums[2] + nums[1] + nums[0]+0 sum(0,3)=nums[3]+nums[2]+nums[1]+nums[0]+0
  • s u m ( 1 , 3 ) = n u m s [ 3 ] + n u m s [ 2 ] + n u m s [ 1 ] + 0 sum(1,3) = nums[3] + nums[2] + nums[1]+0 sum(1,3)=nums[3]+nums[2]+nums[1]+0
  • s u m ( 2 , 3 ) = n u m s [ 3 ] + n u m s [ 2 ] + 0 sum(2,3) = nums[3] + nums[2]+0 sum(2,3)=nums[3]+nums[2]+0
  • s u m ( 3 , 3 ) = n u m s [ 3 ] + 0 sum(3,3) = nums[3] + 0 sum(3,3)=nums[3]+0

d p [ 2 ] dp[2] dp[2] 就是从0-2的最大的子数列的和,如果,前面最大的子数列和都是负数,那就没有再加上它的必要了,直接重新开始算了, d p [ 3 ] = n u m s [ 3 ] dp[3] = nums[3] dp[3]=nums[3] 因为,一个数加上一个负数,只能比它之前更小。

所以,可以确定转移方程为:

确定转移方程

d p [ j ] = { d p [ j − 1 ] + n u m s [ j ] , d p [ j − 1 ] > 0 n u m s [ j ] , d p [ j − 1 ] ≤ 0 dp[j] = \left\{\begin{array}{ll}dp[j-1]+n u m s[j], & d p[j-1]>0 \\nums[j], &dp[j-1] \leq 0\end{array}\right. dp[j]={dp[j1]+nums[j],nums[j],dp[j1]>0dp[j1]0

所以,dp数组里面存储的就是, [ 0 − 0 ] [0-0] [00], [ 0 − 1 ] [0-1] [01], [ 0 − 2 ] [0-2] [02], [ 0 − 3 ] [0-3] [03]等范围内以 n u m s [ i ] nums[i] nums[i]为结尾的最大子数列的和,所以,最后直接遍历一边dp数组即可获得所有该该范围内的最大和

比如:

  • d p [ 2 ] > 0 dp[2]>0 dp[2]>0时, d p [ 3 ] = n u m s [ 3 ] + d p [ 2 ] dp[3] = nums[3] + dp[2] dp[3]=nums[3]+dp[2];
  • d p [ 2 ] < 0 dp[2]<0 dp[2]<0时, d p [ 3 ] = n u m s [ 3 ] dp[3] = nums[3] dp[3]=nums[3];

都是以 n u m s [ 3 ] nums[3] nums[3]来结尾的。

💕
n u m s [ 0 ] nums[0] nums[0]结尾的最大子串和 d p [ 0 ] dp[0] dp[0]
n u m s [ 1 ] nums[1] nums[1]结尾的最大子串和 d p [ 1 ] dp[1] dp[1]
n u m s [ 2 ] nums[2] nums[2]结尾的最大子串和 d p [ 2 ] dp[2] dp[2]
n u m s [ 3 ] nums[3] nums[3]结尾的最大子串和 d p [ 3 ] dp[3] dp[3]
n u m s [ 4 ] nums[4] nums[4]结尾的最大子串和 d p [ i ] dp[i] dp[i]

DP代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
class Solution{
public:
        //返回子序列和的最大值
        int max_sum(vector<int> nums){
            vector<int> dp = result_dp(nums);
            int n = nums.size();
            int maxx = INT_MIN;
            for (int i = 0; i < n; i++) 
            {
                maxx = max(maxx,dp[i]);
            }
            return maxx;
        }
        // 返回子序列
        string max_list(vector<int> nums){
            int r;
            vector<int> dp = result_dp(nums);
            int n = nums.size();
            int maxx = INT_MIN;
            for (int i = 0; i < n; i++) 
            {
                if(maxx<dp[i]){
                    r = i;
                    maxx = dp[i];
                }
            }
            int l = 0;
            l = r;
            int sum = 0;
            int maxsum = max_sum(nums);
            while(1)
            {
                sum += nums[l];
                if(sum==maxsum){break;}
                l--;
            }
            string ans="";
            for(int i=l; i<=r; i++){
                ans = ans + ' ' + to_string(nums[i]);
            }
            return ans;
        }
private:
        vector<int> result_dp(vector<int> nums){
            int n = nums.size();
            vector<int> dp(n,0);
            dp[0] = nums[0];
            for(int i=1; i<n; i++){
                if(dp[i-1]>0){
                    dp[i] = dp[i-1] + nums[i];
                }
                else{
                    dp[i] = nums[i];
                }
            }
            return dp;
        }
};
void Init(vector<int>& nums)
{
    for(int i=0; i<nums.size(); i++){
        cin>>nums[i];
    }  
}
int main()
{                
    int n;
    cin>>n;
    vector<int> nums(n);
    Init(nums);
    Solution solu;
    int mmax = solu.max_sum(nums);  
    string ans = solu.max_list(nums);     
    printf("最大子数列的和为:%d\n",mmax);
    cout<<"子数列为:"<<ans;          
    return 0;                   
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷歌出品,数据集搜索引擎上线了!

文 | 小戏记得在刚入门 ML 时&#xff0c;希望找到一个关于特定领域下的数据集&#xff0c;涉世未深的我在中文互联网不断搜索&#xff0c;可每每点进链接出来的都是某 SDN 下载的高价勒索。用惯了直接从老师同学那里讨来的数据集的我第一次感受到了“寻找数据集”这样一个简单…

【并查集】实现思路及例题

一、应用场景 用于处理不相交集合的合并和查询问题 示例&#xff1a; n 个元素&#xff08;分属不同的的 n 个集合&#xff09;&#xff0c;进行两种操作&#xff1a; 并 —— 给出两个元素的关系&#xff0c;合并两个集合查 —— 查询两个元素是否在同一个集合 二、并查集…

「数据密集型系统搭建」原理篇|用什么方式存储数据最合适

本篇来聊聊数据存储的内容&#xff0c;看看程序世界里数据是以什么形式存在的&#xff1f;为了描述数据并把它们和这个现实世界关联起来我们一般都是如何去进行表达的&#xff1f;最后通过我们习惯的表达方式再结合数据结构是如何存储下来的&#xff1f; 在进行技术方案设计的时…

分享102个PHP源码,总有一款适合您

PHP源码 分享117个PHP源码&#xff0c;总有一款适合您 PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1Ike0x99BcMfZPy6tFSpM9w?pwdzqem 提取码&#xff1a;zqem import os from time import sleepimport requests from bs4 import BeautifulSoup from docx import D…

Linux 系统Bash的常用功能

了解了基本的Linux文件文件系统的概念后,我们将更深入的了解一下Linux的其他方面的内容,那就是我们所使用的用户接口,也就是大家常听到的 Shell ,是一种Linux的命令接口,在 Linux 的世界中,默认使用的是 GNU 开发出来的 shell ,称为 BASH Shell,简单来说,我们之前使用的几个命令…

10.JS笔记-对象

1、什么是对象 对象是一个具体的事物&#xff0c;在js中&#xff0c;对象是一组无序的属性和方法的集合 属性&#xff1a;事物的特征 方法&#xff1a;事物的行为 2、创建对象 利用字面量创建对象利用new Object创建对象利用构造函数创建对象 2.1 变量、属性和方法、函数的…

人工智能的核心技术是什么?

&#xff08;本文阅读时间&#xff1a;5分钟&#xff09;人工智能的核心技术是它的算法被广泛认可的「算法」专业定义是&#xff1a;算法是模型分析的一组可行的&#xff0c;确定的&#xff0c;有穷的规则。基于规则的人工智能上个世纪六七十年代出现的早期人工智能系统都是基于…

VueJs中如何自定义hooks(组合式)函数

前言在Vue当中,一个非常重要的功能就是组件的复用,编写Vue组件,更多的也是在拼装组件,将页面的各个功能进行模块化便于维护和管理,而在项目里,有些页面中的组件的逻辑功能是一样的,如果没有进行功能逻辑的复用,那么每个页面都需要重复的写一遍在Vue当中各个组件是保持独立的,如…

一份职业游戏3D建模师日常工作流程列表,看完不信还有人说建模门槛低

随着游戏行业的发展&#xff0c;越来越多的人开始对这个行业感兴趣&#xff0c;因此有很多的小伙伴梦想成为一个游戏模型师&#xff0c;成为游戏行业里的一员。但是很多人都对这个工作具体是做什么的并不是很了解&#xff0c;下面&#xff0c;我们就来说说游戏模型师的主要工作…

Word处理控件Aspose.Words功能演示:使用 C# 将 DOCX 转换为 HTML

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

Javascript:Class构造函数

为什么需要class 在其他语言中class已经是一个早就被实现的功能&#xff0c;在JavaScript中一直到ES6被实现。在class没有实现之前我们是这样写的&#xff08;如下代码&#xff09; function Person(name,sex){this.name this.sex } Person.prototype.sayfunction(){alert(h…

金融类的APP该如何进行ASA推广

移动理财成为新金融的主流&#xff0c;在如今&#xff0c;金融机构都在争相推出自己的移动理财产品&#xff0c;那今天柚鸥ASO就来给大家讲一下&#xff0c;金融类的APP在进行ASA前的一些注意事项。 APP进行ASA前的开户有两种&#xff1a; 自主开户&#xff08;1&#xff0c;…

检测物理内存容量

文章目录前言前置知识BIOS 中断 0x15 子功能 0xe820 获取内存代码说明实验操作前言 本博客记录《操作系统真象还原》第五章实验操作~ 实验环境&#xff1a;ubuntu18.04VMware &#xff0c; Bochs下载安装 实验内容&#xff1a;三种检测内存的方法。 实验原理&#xff1a; …

volaile关键字详解!

文章目录什么是volatile &#xff1f;volatile三大特性volatile如何使用volatile保证可见性volatile不保证原子性volatile禁止指令重排volatile总结什么是volatile &#xff1f; volatile是一个Java关键字volatile是Java虚拟机提供的轻量级的同步机制 volatile三大特性 保证…

【服务器搭建个人网站】教程五:手把手教你怎样进行公安备案 快来学~

前言 购买一台服务器&#xff0c;再来个域名&#xff0c;搭建一个自己的个人博客网站&#xff0c;把一些教程、源码、想要分享的好玩的放到网站上&#xff0c;供小伙伴学习玩耍使用。我把这个过程记录下来&#xff0c;想要尝试的小伙伴&#xff0c;可以按照步骤&#xff0c;自己…

【Qt】将QtDesigner生成的.ui文件转化为.h头文件

【Qt】将QtDesigner生成的.ui文件转化为.h头文件1、背景2、实例3、附件1、背景 操作系统&#xff1a;windows10专业版。 Qt版本&#xff1a;qt-opensource-windows-x86-msvc2013_64-5.7.1.exe 博主的Qt安装目录&#xff1a;E:\E01_cppIDE\E01_qt\install 并将安装后的bin目录…

校园wifi网页认证登录入口

一、校园WIFI自助服务简介在我校校园网认证业务中&#xff0c;教职工校园通行证账号支持最大3个终端同时在线&#xff0c;如果超出最大在线数&#xff0c;最后上线的终端会把第一个上线的终端踢下线&#xff0c;导致终端经常掉线&#xff0c;需要重新登录才可上网。那么&#x…

【Linux】基础:基础IO

【Linux】基础&#xff1a;基础IO 摘要&#xff1a;本文基础IO的内容将从过往熟悉的C语言文件操作出发&#xff0c;引申指系统调用的文件操作&#xff0c;再进一步深化为对于进程管理的文件进行介绍&#xff0c;从而了解文件描述符的概念和管理方式&#xff0c;其中还会介绍其运…

2022 IoTDB Summit:京东刘刚《Apache IoTDB 在京东万物互联场景中的应用》

12 月 3 日、4日&#xff0c;2022 Apache IoTDB 物联网生态大会在线上圆满落幕。大会上发布 Apache IoTDB 的分布式 1.0 版本&#xff0c;并分享 Apache IoTDB 实现的数据管理技术与物联网场景实践案例&#xff0c;深入探讨了 Apache IoTDB 与物联网企业如何共建活跃生态&#…

基于JavaSpringMvc+mybatis实现学生信息管理系统

基于JavaSpringMvcmybatis实现学生信息管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系…