【leetcode】动态规划::前缀和

标题:【leetcode】前缀和

@水墨不写bug


正文开始:

(一)简单前缀和

描述

给定一个长度为n的数组a1​,a2​,....an​.

接下来有q次查询, 每次查询有两个参数l, r.

对于每个询问, 请输出al​+al+1​+....+ar​

输入描述:

第一行包含两个整数n和q.

第二行包含n个整数, 表示a1​,a2​,....an​.

接下来q行,每行包含两个整数   l和r.

1≤n,q≤10^5
-10^9≤a[i]≤10^9
1≤l≤r≤n

输出描述:

输出q行,每行代表一次查询的结果.

示例1

输入:

3 2
1 2 4
1 2
2 3

输出:

3
6

         思路一: 暴力求解,时间复杂度O(N^2),看到下方的数据量,显然是会超时的算法;

        思路二:动态规划

        动态规划是一种解决多阶段决策问题的数学优化方法。它将原问题分解为若干个子问题,并把子问题的解保存起来,避免重复计算,从而减少计算量。动态规划通常适用于具有重叠子问题和最优子结构的问题,通过构建一个递推关系式,将问题的最优解表示成子问题的最优解的组合。通过自底向上的方式,从子问题的最优解逐步推导出原问题的最优解。动态规划可以大大提高问题的求解效率,但也需要额外的空间来存储子问题的解。

        通过这一思想,我们可以创建一个dp数组:它的每一个下标i对应的位置存储和它对应下标的arr的前(i)个元素和:

        这时若要求得 [l,r] 闭区间的两个下标之间的元素之和,只需对dp数组操作即可。解题关键就是找到如何构造dp数组的方法,以及如何利用dp数组来得到结果。

        本题构建dp数组的策略比较简单:dp数组的前一项加上arr数组的本项即可得到dp数组的本项:

dp[k] = dp[k-1] + arr[k]

        利用dp数组的方法也很简单:输出dp的r下标和dp的(l - 1)下标之和即可:

cout<<(long)dp[r]-(long)dp[l-1]<<endl;

        时间复杂度O(q);

       

        考虑到题目需要做加法,并且数据量达到10 ^5,数据返回范围达到了10^9量级,所以数据类型开成long

参考代码: 

#include <iostream>
#include <vector>
using namespace std;
int main() {int n,q;cin>>n>>q;vector<long> arr(n+1);for(int i = 1;i < n+1;i++) cin>>arr[i];vector<long> dp(n+1);//构造数组dpfor(int k = 1;k < n+1;k++) dp[k] = dp[k-1] + arr[k];for(int j = 0;j < q;j++)//主逻辑{int l = 0,r = 0;cin>>l>>r;cout<<(long)dp[r]-(long)dp[l-1]<<endl;}
}

(二)二维前缀和

描述

给你一个 n 行 m 列的矩阵 A ,下标从1开始。

接下来有 q 次查询,每次查询输入 4 个参数 x1 , y1 , x2 , y2

请输出以 (x1, y1) 为左上角 , (x2,y2) 为右下角的子矩阵的和,

输入描述:

第一行包含三个整数n,m,q.

接下来n行,每行m个整数,代表矩阵的元素

接下来q行,每行4个整数x1, y1, x2, y2,分别代表这次查询的参数

1≤n,m≤1000
1≤q≤10^5
−109≤a[i][j]≤10^9
1≤x1​≤x2​≤n
1≤y1​≤y2​≤m

输出描述:

输出q行,每行表示查询结果。

示例1

输入:

3 4 3
1 2 3 4
3 2 1 0
1 5 7 8
1 1 2 2
1 1 3 3
1 2 3 4

输出:

8
25
32

备注:

读入数据可能很大,请注意读写时间。

         经过了一维前缀和的铺垫,对前缀和有了一些初步理解,在解决二维前缀和就容易多了:

        题目要求是返回两个坐标之间的元素和:

思路一:暴力求解,时间复杂度O(q*N^2),显然是会超时的算法。

 思路二:用动态规划思想,采用二维前缀和方法。

        首先,我们可以定义一个辅助矩阵dp,其中dp[i][j]表示以(1,1)为左上角,(i,j)为右下角的子矩阵的和。

                                (下标为0的行和列不使用)

        接下来,我们可以通过以下方程来计算dp数组:

dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + A[i][j]

        其中,dp[i-1][j]表示以(1,1)为左上角,(i-1,j)为右下角的子矩阵的和; dp[i][j-1]表示以(1,1)为左上角,(i,j-1)为右下角的子矩阵的和; dp[i-1][j-1]表示以(1,1)为左上角,(i-1,j-1)为右下角的子矩阵的和; A[i][j]表示矩阵A的元素。

        对于每次查询(x1, y1)(x2, y2),我们可以通过如下公式计算子矩阵的和:

sum = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]

        其中,dp[x2][y2]表示以(1,1)为左上角,(x2,y2)为右下角的子矩阵的和; dp[x1-1][y2]表示以(1,1)为左上角,(x1-1,y2)为右下角的子矩阵的和; dp[x2][y1-1]表示以(1,1)为左上角,(x2,y1-1)为右下角的子矩阵的和; dp[x1-1][y1-1]表示以(1,1)为左上角,(x1-1,y1-1)为右下角的子矩阵的和。

        其实前缀和是一类题型,我们可以在一定程度上记忆状态转移方程(上述的dp表构建方程,使用dp表方程), 但是我认为更重要的是理解方程为什么是这样的结构,以及方程的推导由来:

         

参考代码: 

#include <iostream>
#include<vector>
using namespace std;int main(){int n = 0,m = 0,q = 0;cin>>n>>m>>q;vector<vector<int>> arr(n+1,vector<int>(m+1));for(int i = 1;i <= n ; i++)//读取数据for(int j = 1;j <= m ; j++)cin>>arr[i][j];vector<vector<long>> dp(n+1,vector<long>(m+1));//long防止溢出for(int i = 1;i <= n ; i++)//建表for(int j = 1;j <= m ; j++)dp[i][j] = dp[i-1][j] + dp[i][j-1] + arr[i][j] - dp[i-1][j-1];for(int c = 0;c<q;c++)//c仅仅起计数作用{int i0 = 0,j0 = 0,i = 0,j = 0;cin>>i0>>j0>>i>>j;long ret = dp[i][j] - dp[i][j0-1] - dp[i0-1][j]+dp[i0-1][j0-1];cout<<ret<<endl;}
}
// 64 位输出请用 printf("%lld")

(三)寻找数组的中心下标

给你一个整数数组 nums ,请计算数组的 中心下标 

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

示例 1:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

示例 2:

输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

示例 3:

输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。

提示:

  • 1 <= nums.length <= 10^4
  • -1000 <= nums[i] <= 1000

class Solution {
public:int pivotIndex(vector<int>& nums) {int n = nums.size();vector<long> f(n), g(n);for(int i = 1;i < n;i++)//建f,g表f[i] = f[i-1] + nums[i-1];//不赋值默认为0for(int i = n-2;i >= 0;i--)g[i] = g[i+1] + nums[i+1];for(int k = 0;k < n;k++)if(f[k]==g[k]) return k;return -1;}
};

 (四)除自身外数组的乘积

        给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

        题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

        请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

提示:

  • 2 <= nums.length <= 10^5
  • -30 <= nums[i] <= 30
  • 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内
class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int n = nums.size();vector<int> f(n), g(n);f[0] = g[n-1] = 1;//乘法操作不能初始化为0for(int i = 1;i < n;i++)//建f,g表f[i] = f[i-1] * nums[i-1];//不赋值默认为0for(int i = n-2;i >= 0;i--)g[i] = g[i+1] * nums[i+1];vector<int> ret;for(int i = 0;i < n;i++)ret.push_back(f[i]*g[i]);return ret;}
};

完~

未经作者同意禁止转载

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

yarn集群部署

yarn集群部署案例 我们来基于一个案例讲解yarn集群部署 我们要部署yarn集群&#xff0c;需要分别部署HDFS文件系统及YARN集群 Hadoop HDFS分布式文件系统&#xff0c;我们会启动&#xff1a; NameNode进程作为管理节点DataNode进程作为工作节点SecondaryNamenode作为辅助 同…

微信小程序实现滚动标签

使用scroll-view标签可实现组件滚动标签 1、list中 list.wxml代码如下: <!--pages/list/list.wxml--> <navigation-bartitle"小程序" back"{{false}}"color"black" background"#FFF"></navigation-bar><scroll-…

Kibana管理ES生命周期

希望通过Kibana界面管理ES索引的生命周期 版本&#xff1a;7.15.2 创建索引模板 创建索引模板方便匹配索引&#xff0c;对匹配到的一批索引采用同一套生命周期管理策略&#xff0c;例如开发环境的所有索引以dev-开头&#xff0c;可以创建样式为dev-*的索引模板以匹配开发环境…

13 Python进阶:pip及其他常用模块

pip 是 Python 包管理工具&#xff0c;它提供了对 Python 包的查找、下载、安装、卸载的功能。 包地址&#xff1a; https://pypi.org/ 最新的 Python 版本已经预装了 pip。 pip 各种命令 查看是否已经安装 pip 可以使用以下命令&#xff1a; pip --version下载安装包使用…

42.基于SpringBoot + Vue实现的前后端分离-服装销售平台管理系统(项目 + 论文)

项目介绍 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的交换和信息流通显得特别重要。因此&#xff0c;开发合适的服装销售平台成为企业必然要走的一步棋。开发合适的服…

医疗器械FDA | 常见的网络安全材料发补问题都有哪些?

FDA网络安全资料发补咨询点此​​获取https://work.weixin.qq.com/ca/cawcde5ee29d239046 ————————--- 01 安全文档编写问题 FDA网络安全文档编写格式、内容、可读性等未满足官方要求&#xff0c;则将可能被要求发补整改编写后的文档。 02 安全管理问题 a. 网络安…

websocket实践

文章目录 背景WebSocket API使用场景优点 实例步骤 1: 设置 WebSocket 服务器步骤 2: 创建客户端 HTML 页面步骤 3: 测试 WebSocket 通信注意事项实际操作 参考资料 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它使得浏览器和服务器只需建立一个连接&#xff0c;…

怎么保证缓存与数据库的最终一致性?

目录 零.读数据的标准操作 一.Cache aside Patten--旁路模式 二.Read/Write Through Pattern--读写穿透 三.Write Back Pattern--写回 四.运用canal监听mysql的binlog实现缓存同步 零.读数据的标准操作 这里想说的是不管哪种模式读操作都是一样的&#xff0c;这是一种统一…

Linux安装最新版Docker完整教程

参考官网地址&#xff1a;Install Docker Engine on CentOS | Docker Docs 一、安装前准备工作 1.1 查看服务器系统版本以及内核版本 cat /etc/redhat-release1.2 查看服务器内核版本 uname -r这里我们使用的是CentOS 7.6 系统&#xff0c;内核版本为3.10 1.3 安装依赖包 …

每日OJ题_两个数组dp①_力扣1143. 最长公共子序列

目录 力扣1143. 最长公共子序列 解析代码 力扣1143. 最长公共子序列 1143. 最长公共子序列 难度 中等 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样…

能不能换DB吗?--抽象工厂模式

1.1 就不能不换DB吗&#xff1f; 都是换数据库惹的祸。 "我们团队前段时间用.net的C#来开发好一个项目&#xff0c;是给一家企业做的电子商务网站&#xff0c;是用SQL Server作为数据库的&#xff0c;应该说上线后除了开始有些小问题&#xff0c;基本都还可以。而后&#…

33. UE5 RPG使用增强输入激活GameplayAbility(三)

在前面的文章&#xff0c;我们实现了使用GameplayTag和InputAction的对应绑定的数据&#xff0c;并且添加到了增强输入映射的上下文中&#xff0c;实现了通过按键打印对应的GameplayTag&#xff0c;这只是我们基础需要制作的。目的主要是为了实现在GameplayAblity上面设置对应的…