当前位置:   article > 正文

算法思想总结:前缀和算法

算法思想总结:前缀和算法

                                                    创作不易,感谢三连 !!

一、【模版】前缀和

牛客网—【模版】前缀和

  1. #include<iostream>
  2. #include<vector>
  3. using namespace std;
  4. int main()
  5. {
  6. int n,q;
  7. cin>>n>>q;
  8. //创建数组 下标从1开始
  9. vector<int> arr(n+1);
  10. for(int i=1;i<=n;++i) cin>>arr[i];
  11. //创建一个前缀和数组
  12. vector<long long> dp(n+1);//默认初始化的时候是0
  13. for(int i=1;i<=n;++i) dp[i]=arr[i]+dp[i-1];
  14. //使用前缀和数组
  15. int l,r;
  16. while(q--)
  17. {
  18. cin>>l>>r;
  19. cout<<dp[r]-dp[l-1]<<endl;
  20. }
  21. return 0;
  22. }

二、【模板】二维前缀和

牛客网—【模版】二维前缀和

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. int main()
  5. {
  6. int n,m,q;
  7. cin>>n>>m>>q;
  8. //根据m和n创建二维数组
  9. vector<vector<int>> arr(n+1,vector<int>(m+1));
  10. for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) cin>>arr[i][j];
  11. //创建前缀和矩阵
  12. vector<vector<long long>> dp(n+1,vector<long long>(m+1));//防止溢出
  13. for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
  14. dp[i][j]=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1];
  15. //使用前缀和矩阵
  16. int x1,y1,x2,y2;
  17. while(q--)
  18. {
  19. cin>>x1>>y1>>x2>>y2;
  20. cout<<dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]<<endl;
  21. }
  22. }

三、寻找数组的中间下标

. - 力扣(LeetCode)寻找数组的中间下标

  1. class Solution {
  2. public:
  3. int pivotIndex(vector<int>& nums)
  4. {
  5. int n=nums.size();
  6. //创建一个前缀和数组
  7. vector<int> dpq(n);
  8. for(int i=1;i<n;++i) dpq[i]=nums[i-1]+dpq[i-1];
  9. //创建一个后缀和数组
  10. vector<int> dph(n);
  11. for(int i=n-2;i>=0;--i) dph[i]=nums[i+1]+dph[i+1];
  12. //使用前缀和和后缀和数组
  13. for(int i=0;i<n;++i) if(dpq[i]==dph[i]) return i;
  14. return -1;
  15. }
  16. };

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

. - 力扣(LeetCode)除自身以外数组的乘积

  1. class Solution {
  2. public:
  3. vector<int> productExceptSelf(vector<int>& nums)
  4. {
  5. int n=nums.size();//记录有效个数
  6. //创建一个前缀乘积数组
  7. vector<int> dpq(n,1);
  8. for(int i=1;i<n;++i) dpq[i]=nums[i-1]*dpq[i-1];
  9. //创建一个后缀乘积数组
  10. vector<int> dph(n,1);
  11. for(int i=n-2;i>=0;--i) dph[i]=nums[i+1]*dph[i+1];
  12. //使用这两个数组并创建answer
  13. vector<int> answer(n);
  14. for(int i=0;i<n;++i) answer[i]=dpq[i]*dph[i];
  15. return answer;
  16. }
  17. };

五、 和为k的子数组

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. int subarraySum(vector<int>& nums, int k)
  4. {
  5. unordered_map<int,int> hash;//用来统计前缀和的个数
  6. hash[0]=1;//默认有一个前缀和为0的数组
  7. int sum=0;//记录前缀和
  8. int ret=0;//记录有多少个和为k的子数组
  9. for(auto e:nums)
  10. {
  11. sum+=e;
  12. if(hash.count(sum-k)) ret+=hash[sum-k];
  13. ++hash[sum];
  14. }
  15. return ret;
  16. }
  17. };

六、和可被k整除的子数组

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. int subarraysDivByK(vector<int>& nums, int k)
  4. {
  5. unordered_map<int,int> hash;//用来统计前缀和%k的余数
  6. hash[0]=1;
  7. int sum=0;//统计前缀和
  8. int ret=0;//统计子数的个数
  9. for(auto e:nums)
  10. {
  11. sum+=e;
  12. int temp=(sum%k+k)%k;//确保temp为正数
  13. if(hash[temp]) ret+=hash[temp];//用ret去统计符合要求的子数组个数
  14. ++hash[temp];//将该sum对应的余数继续存进去
  15. }
  16. return ret;
  17. }
  18. };

七、连续数组

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. //将数组中为0的改成-1.找到一个最长子数组 其元素和结果恰好为0
  4. int findMaxLength(vector<int>& nums)
  5. {
  6. unordered_map<int,int> hash;//用来统计前缀和的下标
  7. hash[0]=-1;//[0,x-1]之间是否有前缀和是sum,如果x=0,那么x-1=-1,也解释了为什么hash[0]=-1
  8. int sum=0;
  9. int ret=0;//用来更新最长的长度
  10. for(int i=0;i<nums.size();++i)
  11. {
  12. sum+=nums[i]==0?-1:1;
  13. if(hash.count(sum)) ret=max(ret,i-hash[sum]);
  14. else hash[sum]=i;//只需要存储最早之前的下标就可以了,因为我们要找的是最长的
  15. }
  16. return ret;
  17. }
  18. };

八、最大子数组的和

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. int maxSubArray(vector<int>& nums)
  4. {
  5. int sum=0,Max=INT_MIN,Min=0;
  6. for(auto e:nums)
  7. {
  8. sum+=e;
  9. Max=max(Max,sum-Min);
  10. Min=min(Min,sum);
  11. }
  12. return Max;
  13. }
  14. };

九、矩阵区域和

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k)
  4. {
  5. int m=mat.size(),n=mat[0].size();
  6. //根据矩阵创建一个前缀和数组
  7. vector<vector<int>> dp(m+1,vector<int>(n+1));
  8. //开始对矩阵进行值的输入
  9. for(int i=1;i<=m;++i)
  10. for(int j=1;j<=n;++j)
  11. dp[i][j]=dp[i-1][j]+dp[i][j-1]+mat[i-1][j-1]-dp[i-1][j-1];
  12. //开始使用前缀和数组 输入返回的数组
  13. vector<vector<int>> answer(m,vector<int>(n));//需要返回的数组
  14. for(int i=0;i<m;++i)
  15. for(int j=0;j<n;++j)
  16. {
  17. int x1=max(i-k,0)+1,y1=max(j-k,0)+1;
  18. int x2=min(i+k,m-1)+1,y2=min(j+k,n-1)+1;
  19. answer[i][j]=dp[x2][y2]+dp[x1-1][y1-1]-dp[x2][y1-1]-dp[x1-1][y2];
  20. }
  21. return answer;
  22. }
  23. };

十、前缀和算法总结

1、前缀和并不一定真的需要搞一个前缀和数组

2、用哈希存前缀和相关数据的话要注意hash[0]

3、无论dp数组是开的n还是开的n+1,都要注意和原数组之间的映射关系

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/279275
推荐阅读
相关标签
  

闽ICP备14008679号