【基础算法】前缀和

1.【模板】前缀和

前缀和

思路:

板子题,思路前缀和数组记录 。

虽是板子,但以后切记不要死记模版。

主要要开long long,防溢出。

#include <iostream>
using namespace std;

#include<vector>

int main() 
{
    int n = 0, q = 0;
    cin >> n >> q;
    vector<int> arr(n);
    for(int i = 0; i < n; i++) cin >> arr[i];
    //填充前缀和数组
    vector<long long> dp(n + 1);
    for(int i = 1; i <= n; i++) dp[i] = dp[i - 1] + arr[i - 1];
    //q次查询
    while(q--)
    {
        int l = 0, r = 0;
        cin >> l >> r;
        cout << dp[r] - dp[l - 1] << endl;
    }
    return 0;
}

 2.【模板】二维前缀和

二维前缀和

思路:

二维的模板题。

此图借助理解

 

#include <iostream>
using namespace std;

#include<vector>

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];
    //填充dp表
    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][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1] + arr[i][j];
    while(q--)
    {
        int x1 = 0, y1 = 0, x2 = 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;
    }
    return 0;    
}

3.寻找数组的中心下标 

寻找数组的中心下标

思路:

求解前缀和与后缀和

比较i位置的前缀和与后缀和是否相等 

class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        int n = nums.size();
        //f[i]表示:[0, i - 1]内的所有数的和
        //g[i]表示:[i + 1, n - 1]内的所有数的和
        vector<int> f(n + 1), g(n + 1);
        //预处理前缀和、后缀和数组
        for(int i = 1; i < n; i++) f[i] = f[i - 1] + nums[i - 1];
        for(int i = n - 2; i >= 0; i--) g[i] = g[i + 1] + nums[i + 1];
        for(int i = 0; i < n; i++)
        {
            if(f[i] == g[i]) return i;
        }
        return -1;
    }
};

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

除自身以外数组的乘积

思路:

跟3类似,只是f[i]与g[i]的状态发生了变化,变为了乘法。

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(n);

        //填充dp表
        vector<int> f(n), g(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 i = n - 2; i >= 0; i--) g[i] = g[i + 1] * nums[i + 1];

        for(int i = 0; i < n; i++)
        {
            ans[i] = f[i] * g[i];
        }
        return ans;
    }
};

5.和为k的子数组

和为k的子数组

 思路:

前缀和与哈希表结合,让我们求子数组和为k,即让我们求前缀和的sum - k出现的次数。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int sum = 0, ret = 0;
        unordered_map<int, int> hash; // 记录子数组和为k的个数
        // 细节问题 - 子数组为0时的数量默认为1
        hash[0] = 1;
        for(auto x : nums)
        {
            sum += x;// 计算当前位置的前缀和
            if(hash.count(sum - k)) ret += hash[sum - k];//更新结果
            hash[sum]++;// 统计子数组和的次数
        }
        return ret;
    }
};

6.和可被k整除的子数组

和可被k整除的子数组

思路:

与4类似,只是hash里面存的内容有所不同

同余定理:(sum - x) % p = 0 -> sum % p == x % p.

c++/java中的负数%正数的修正(a%b + b) % b

 

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        unordered_map<int, int> hash;//记录前缀余数出现的次数
        //处理细节问题
        hash[0 % k] = 1;//0 - 代表余数为0出现的次数默认为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;
    }
};

 7.连续数组

连续数组

思路:

与和为k的子数组类似

细节问题比较特殊

哈希表存放的是什么?

hash【0】应该存什么?

class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        //把0都转化为-1,就可以用我们的前缀和求解了-> 实质就是求子数组为k时对应的子数组长度
        unordered_map<int, int> hash;// 存放的是sum的对应下标
        //处理细节问题
        hash[0] = -1;// hash表内存放的是下标,所以前缀和为0只能是-1
        int sum = 0, ret = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            sum += nums[i] == 0 ? -1 : 1; // 计算当前位置的前缀和
            if(hash.count(sum)) ret = max(ret, i - hash[sum]); //更新结果
            else hash[sum] = i; //前缀和进入哈希表
        }
        return ret;
    }
};

 8.矩阵区域和

矩阵区域和

思路:

二维前缀和

细节:处理映射关系上如何处理?

class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        int m = mat.size(), n = mat[0].size();
        //创建dp表
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        //填写dp表
        //注意dp表与mat矩阵的映射关系mat[i - 1][j - 1]还是mat[i][j]
        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>> ret(m, vector<int>(n));
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
            {
                //使dp数组下标映射和原数组对齐
                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;
                ret[i][j] = dp[x2][y2] - dp[x2][y1 - 1] - dp[x1 - 1][y2] + dp[x1 - 1][y1 - 1];
            }
        return ret;
    }
};

 

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花影随风_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值