• 算法专题篇四:前缀和


    "回忆里的我,比国王富有。奢侈的快乐~" 


     1、前缀和【模板】

    (1) 题目解析

    (2) 算法原理         

    1. #include
    2. using namespace std;
    3. const int N = 100010;
    4. // 可能出现溢出
    5. long long arr[N],dp[N];
    6. int n,q;
    7. int main() {
    8. cin >> n >> q;
    9. // 初始化数组
    10. for(int i=1;i<=n;++i) cin >> arr[i];
    11. // 创建dp
    12. for(int i = 1; i <= n; i++) dp[i] = dp[i - 1] + arr[i];
    13. // 查询
    14. while(q--){
    15. int l,r;
    16. cin >> l >> r;
    17. // 计算区间和
    18. cout << dp[r] - dp[l - 1] << endl;
    19. }
    20. return 0;
    21. }

     2、DP35 【模板】二维前缀和

    (1) 题目解析

            同一维前缀和类似,这类题型都需要预处理出前缀和数组,再求和。

    (2) 算法原理         

    1. #include
    2. using namespace std;
    3. const int N = 1010;
    4. int main() {
    5. int n, m, q;
    6. long long arr[N][N], dp[N][N];
    7. cin >> n >> m >> q;
    8. // 输入数组
    9. for (int i = 1; i <= n; ++i)
    10. for (int j = 1; j <= m; ++j)
    11. cin >> arr[i][j];
    12. // 预处理前缀和
    13. for (int i = 1; i <= n; ++i)
    14. for (int j = 1; j <= m; ++j)
    15. dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + arr[i][j] - dp[i - 1][j - 1];
    16. // 查询
    17. int x1,y1,x2,y2;
    18. while(q--)
    19. {
    20. cin >> x1 >> y1 >> x2 >> y2;
    21. cout << dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1] << endl;
    22. }
    23. return 0;
    24. }

    3、寻找数组的中心下标

    (1) 题目解析

            本题的核心在于,存在一个mid,使得sum(0,mid-1) == sum(mid+1,end)。所以,我们只需要求得 (0,mid-1) 区间的值 是否等于 (mid+1,end)的值,就可以判断该mid 是否是中心下标。

    (2) 算法原理         

    1. class Solution {
    2. public:
    3. int pivotIndex(vector<int>& nums) {
    4. int n = nums.size();
    5. vector<int> f(n+1);
    6. vector<int> g(n+1);
    7. // 初始化前缀和
    8. f[0] = g[n-1] = 0;
    9. for(int i =1;i<=n;++i) f[i] = f[i-1] + nums[i-1];
    10. for(int i =n-2;i>=0;--i) g[i] = g[i+1] + nums[i+1];
    11. // 判断 0~n-1 迭代下标
    12. for(int i=0;i
    13. if(f[i] == g[i]) return i;
    14. }
    15. return -1;
    16. }
    17. };

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

    (1) 题目解析        

            

    (2) 算法原理        

    1. class Solution {
    2. public:
    3. vector<int> productExceptSelf(vector<int>& nums) {
    4. int n = nums.size();
    5. vector<int> f(n+1);
    6. vector<int> g(n+1);
    7. // 初始化前缀和
    8. f[0] = g[n-1] = 1;
    9. for(int i=1;i<=n;++i) f[i] = f[i-1]*nums[i-1];
    10. for(int i=n-2;i>=0;--i) g[i] = g[i+1]*nums[i+1];
    11. // 判断
    12. vector<int> ret;
    13. for(int i=0;i
    14. {
    15. ret.push_back(f[i] * g[i]);
    16. }
    17. return ret;
    18. }
    19. };

    5、和为 K 的子数组

    (1) 题目解析

            什么是子数组? 子数组一定是连续的 --> 这本质对应的是前缀和因为前缀和就是连续区间。

    (2) 算法原理         

    1. class Solution {
    2. public:
    3. int subarraySum(vector<int>& nums, int k) {
    4. unordered_map<int,int> hash;
    5. hash[0] = 1; // 细节处理
    6. int sum = 0; // 模拟前缀和
    7. int count = 0;
    8. for(auto& num:nums)
    9. {
    10. sum += num;
    11. if(hash.count(sum-k)){
    12. // 该元素存在
    13. // 有多少个这个元素 就能找出多少个k
    14. count += hash[sum-k];
    15. }
    16. // 该元素值个数++
    17. hash[sum]++;
    18. }
    19. return count;
    20. }
    21. };

    6、和可被 K 整除的子数组

    (1) 题目解析        

             相比于上题,这道题无非条件做了些改变,在数组里找到能被k整除的前缀和。不过在开始这道题之前,我们得补充另外一个知识。

    同余定理:

            给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系

            简单来说,就是这样:

            有了上述的基础,我们再来认识认识这道题。

    (2) 算法原理        

    1. class Solution {
    2. public:
    3. int subarraysDivByK(vector<int>& nums, int k) {
    4. unordered_map<int, int> hash;
    5. hash[0 % k] = 1; // 0 这个数的余数
    6. int sum = 0,ret = 0;
    7. for(auto& n:nums)
    8. {
    9. sum += n;
    10. int r = (sum % k + k) % k; // 修正后的余数
    11. if(hash.count(r)) ret += hash[r];
    12. hash[r]++;
    13. }
    14. return ret;
    15. }
    16. };

    7、连续数组

    (1) 题目解析

        我们可以根据1,-1的性质,因为题目是要找到能够匹配相同数量的0,1数字。当我们将0替换为-1时,如果存在长度相同的0,1数对,那么它们的和为0。反过来,本题就是要找最长子数组元素和为0的长度。     

    (2) 算法原理         

    1. class Solution {
    2. public:
    3. int findMaxLength(vector<int>& nums) {
    4. int n = nums.size();
    5. unordered_map<int,int> hash;
    6. hash[0] = -1;
    7. int sum = 0;
    8. int len = 0;
    9. for(int i = 0; i < nums.size(); i++)
    10. {
    11. sum += nums[i] == 0 ? -1 : 1; // 计算前缀和
    12. if(hash.count(sum)) len = max(len,i - hash[sum]);
    13. else hash[sum] = i; // 存储下标
    14. }
    15. return len;
    16. }
    17. };

    8、矩阵区域和

    (1) 题目解析        

            矩阵和同二维前缀和是相差无几的,都是求区域内的面积。例如上述例子,虽然是求整个矩阵的元素和,但是可以化解为从下标[1,1] 到 下标[3,3] 的前缀和矩阵。

    (2) 算法原理

    计算前缀和:

          

    使用前缀和:

    1. class Solution {
    2. public:
    3. vectorint>> matrixBlockSum(vectorint>>& mat, int k) {
    4. int m = mat.size();
    5. int n = mat[0].size();
    6. // 初始化前缀和
    7. vectorint>> dp(m+1,vector<int>(n+1));
    8. for(int i=1;i<=m;++i)
    9. for(int j=1;j<=n;++j) dp[i][j] = dp[i-1][j] + dp[i][j-1] + mat[i-1][j-1] - dp[i-1][j-1];
    10. // 使用前缀和
    11. vectorint>> ret(m,vector<int>(n));
    12. for(int i=0;i
    13. for(int j=0;j
    14. {
    15. int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1;
    16. int x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1;
    17. ret[i][j] = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1];
    18. }
    19. return ret;
    20. }
    21. };

    本篇到此结束,感谢你的阅读。

    祝你好运,向阳而生~

  • 相关阅读:
    09_面向对象高级_泛型
    Web 异常 + Error
    前端,样式,行间距,字间距
    allatori8.0文档翻译-第十三步:Android Studio整合
    每日OJ题_其它背包问题①_力扣474. 一和零(二维费用01背包)
    Oracle数据库Bug:相关子查询多层嵌套报错:标识符无效
    将CSV、Excel、XML文件转换为MySQL数据库
    【WP】猿人学13_入门级cookie
    Linux系统基础教程:1、xshell进行远程操控
    gcc 编译参数 -fPIC 作用
  • 原文地址:https://blog.csdn.net/RNGWGzZs/article/details/132824274