当前位置: 首页 > news >正文

【C++】前缀和算法习题

在这里插入图片描述
🎆个人主页:夜晚中的人海

在这里插入图片描述

今日语录:谁和我一样用功,谁就会和我一样成功。 —— 莫扎特

文章目录

  • ⭐一、【模板】前缀和
  • 🎄二、【模板】二维前缀和
  • 🎆三、寻找数组的中心下标
  • 🚀四、除自身以外数组的乘积
  • 🚘五、和可被K整除的子数组
  • 🏝️六、连续数组
  • 🏖️七、矩阵区域和

⭐一、【模板】前缀和

题目链接:【模板】前缀和

题目描述:

在这里插入图片描述

解题思路:
1.根据题目要求,我们可以先预处理出一个前缀和的数组

2.dp[i]表示[l , i]区间内所有元素的和,dp[i - 1]表示[l , i - 1]区间内所有区间内的和;因此我们可以得到一个递推公式:dp[i] = dp[i - 1] + arr[i]

3.因此我们可以使用前缀和数组快速求出某一段区间内的和,从而解决问题

代码实现:

#include <iostream>
using namespace std;const int N = 100000;
long long arr[N],dp[N];int main() 
{int n = 0,m = 0;cin >> n >> m;for(int i = 1;i <= n;i++){cin >> arr[i];}for(int j = 1;j <= n;j++){//列举每一位的前缀和dp[j] = dp[j - 1] + arr[j];}while(m--){int l = 0,r = 0;cin >> l >> r;cout << dp[r] - dp[l - 1] << endl;}return 0;
}

🎄二、【模板】二维前缀和

题目链接:【模板】二维前缀和

题目描述:

在这里插入图片描述

解题思路:
1.我们还是向一维数组的方式一样搞出来一个前缀和数组,但在处理二维数组中的边界时有许多条件需要处理,因此我们要在矩阵的最上面和最左边添加上一行和⼀列0,处理结果如下图所示
在这里插入图片描述
2.处理完成后,我们在使用前缀和数组时,下标从1开始,就可以大胆使用 i - 1,j - 1位的值了
注: 由于我们添加了一行和一列的值,在原数组和前缀和数组中,它们的映射关系就发生了变化从前缀和数组中到原数组,横纵坐标 - 1,从数组到前缀和数组中,横纵坐标 + 1

3.我们使用sum[ i ][ j ]来表示从[ 0 ][ 0 ] 到 [ i ][ j ]这一区间内所有元素的和,如下图所示:
在这里插入图片描述
4.我们可以将[ 0 ][ 0 ]位置到[ i ][ j ]位置分成四段区域如下图所示:
在这里插入图片描述
在求解蓝色区域时非常好求,它就是数组中arr[i - 1][j - 1]的位置
在单独求解黄色区域以及绿色区域时不太好求,但如果是红+黄以及红+ 绿这段区域时,那就非常好求了,求出来的三个值累加起来,那就是红 + 黄 + 红 + 绿 + 蓝,我们发现多加了一次红,把红色这块区域减掉一次即可

5.求出上述的前缀和矩阵,我们就可以来使用该矩阵,在使用该矩阵时,我们要注意让每个下标都++,这样才能映射到前缀和矩阵中
在这里插入图片描述
6.根据上图所示,我们要想求蓝色区域的面积,就来分析一下其它几块区域
• 我们可以快速求出整块区域的面积,就是sum[row2][col2],用整块面积去减去其它区域的面积,单独求绿色区域以及黄色区域的面积不太好求,但如果求红 + 黄以及红 + 绿这片区域那就很好求了:红 + 黄:sum[row1 - 1][col2] 红+ 绿:[row2][co1 - 1],由于我们多减了一次红色区域,因此要加上一次红色区域的面积:arr[row1 - 1][col1 - 1],根据上述操作,这样求出来我们所需要蓝色区域的面积了

代码实现:

#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 long>> dp(n + 1,vector<long long>(m + 1));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];}}//使用前缀和数组while(q--){int x1 = 0,x2 = 0,y1 = 0,y2 = 0;cin>>x1>>y1>>x2>>y2;cout<<dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1-1]<<endl;}
}

🎆三、寻找数组的中心下标

题目链接:寻找数组的中心下标

题目描述:
在这里插入图片描述

解题思路:
1.定义两个数组,一个表示前缀和,另一个表示后缀和

2.用一个for循环枚举每一个可能是中心点的下标,如果该下标的前缀和和后缀和都相等,则返回;循环结束后若还是没找到,则返回-1

代码实现:

class Solution {
public:int pivotIndex(vector<int>& nums) {int n = nums.size();vector<int> f(n),g(n);//计算前缀和for(int i = 1;i < n;i++){f[i] = f[i - 1] + nums[i - 1];}//计算后缀和for(int j = n - 2;j >= 0;j--){g[j] = g[j + 1] + nums[j + 1];}for(int i = 0;i < n;i++){if(f[i] == g[i])return i;}return -1;}
};

🚀四、除自身以外数组的乘积

题目链接:除自身以外数组的乘积

题目描述:
在这里插入图片描述

解题思路:
1.跟前缀和思路算法类型,定义两个数组,一个表示前缀和的乘积,另一个表示后缀和的乘积

2.遍历一次数组,根据题目要求返回结果即可

代码实现:

class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int n = nums.size();vector<int> f(n),g(n),answer(n);f[0] = 1,g[n - 1] = 1;//前缀乘积for(int i = 1;i < n;i++){f[i] = f[i - 1] * nums[i - 1];}//后缀乘积for(int j = n - 2;j >= 0;j--){g[j] = g[j + 1] * nums[j + 1];}//结果for(int i = 0;i < n;i++){answer[i] = f[i] * g[i];}return answer;}
};

🚘五、和可被K整除的子数组

题目链接:和可被K整除的子数组

题目描述:

在这里插入图片描述

解题思路:
1.在解决此类问题时,我们需要补充两个知识点:
同余定理: 如果(a - b) % n == 0 ,那么我们可以得到⼀个结论: a % n == b % n
负数取模: 为了避免出现负数的结果,我们以(a % n + n) % n 的形式来确保结果是正数

2.根据上述的补充,我们就只需要求出有多少个前缀和的余数等于sum % k即可,使用⼀个哈希表,一边求当前位置的前缀和,⼀边存下之前每⼀种前缀和出现的次数

代码实现:

class Solution {
public:int subarraysDivByK(vector<int>& nums, int k) {int n = nums.size();unordered_map<int,int> hash;//处理特殊情况hash[0 % k] = 1;int sum = 0,ret = 0;for(auto x : nums){sum += x;int r = (sum % k + k) % k;if(hash.count(r))ret += hash[r];hash[r]++;}return ret;}
};

🏝️六、连续数组

题目链接:连续数组

题目描述:

在这里插入图片描述

解题思路:
1.题目要求我们要找具有相同数量的0和1,我们可以转变一下思路,将0转变成-1,从而改变寻找一段区间和为0的区域

2.使用哈希表记录当前位置的前缀和以及第一次出现该前缀和的位置

代码实现:

class Solution {
public:int findMaxLength(vector<int>& nums) {int n = nums.size();unordered_map<int,int> hash;hash[0] = -1;int sum = 0,ret = 0;for(int i = 0;i < n;i++){sum += nums[i] == 0 ? -1 : 1;if(hash.count(sum))ret = max(ret,i - hash[sum]);elsehash[sum] = i;}return ret;}
};

🏖️七、矩阵区域和

题目链接:矩阵区域和

题目描述:
在这里插入图片描述

解题思路:
1.使用两个数组,一个用来预处理前缀和的数组,另一个作为返回结果的数组

2.根据前面二维矩阵的相关解法,最需要注意的就是两数组的映射关系问题,弄清楚它们之间的映射关系,我们就可以快速的解决此类问题

代码实现:

class Solution {
public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {int m = mat.size();int n = mat[0].size();vector<vector<int>> dp(m + 1,vector<int>(n + 1));//预处理前缀和数组for(int i = 1;i <= m;i++){for(int j = 1;j <= n;j++){dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1]+ mat[i - 1][j - 1];}}//使用前缀和数组vector<vector<int>> answer(m,vector<int>(n));for(int i = 0;i < m;i++){for(int j = 0;j < n;j++){//防止超越矩阵的范围int x1 = max(0,i - k) + 1,y1 = max(0,j - k) + 1;int x2 = min(m - 1,i + k) + 1,y2 = min(n - 1,j + k) + 1;answer[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];}}return answer;}
};
http://www.dtcms.com/a/532409.html

相关文章:

  • GitHub等平台形成的开源文化正在重塑加特
  • 基于单片机的家庭防盗防火智能门窗报警系统设计
  • 响应式网站建设的未来发展网络规划与设计就业前景
  • 【图像处理】图像错切变换
  • Docker环境离线安装-linux服务器
  • 软件设计师知识点总结:结构化开发
  • 持续改变源于团队学习
  • Unity安装newtonsoft
  • Spring Boot3零基础教程,整合 Redis,笔记69
  • 凡科网站官网登录入口wordpress 列表模板
  • 读论文AI prompt
  • 《Ionic 平台:全面解析与深入探讨》
  • 网站做淘宝客有什么要求微网站的优势
  • FFmpeg 基本数据结构 AVStream分析
  • kafka数据同步一致性问题
  • 淘宝网站的建设内容网站建设的平台
  • Linux进程间通信:管道与System V IPC的全解析
  • 技术选型对比:几种主流方案获取淘宝商品实时数据的优劣分析
  • 想象力网站建设深圳市房地产信息系统平台
  • 让 Codex 用 React 生成一个博客首页
  • Kafka的概念
  • 开发实战 - ego商城 - 补充:小程序性能优化
  • 如何截取PDF内容为图片
  • 智慧景区导览小程序
  • 58同城企业网站怎么做的wordpress 评论设计
  • 珠海建站模板怎么做多语言网站
  • 致敬哈耶克,让灯火照亮个人前行的道路
  • 【LeetCode100】--- 96.只出现一次的数字【思维导图+复习回顾】
  • 网络编程Socket套接字
  • 算法基础篇(9)倍增与离散化