C++ 前缀和

目录

目录

前缀和

二维前缀和

寻找数组的中心下标

除自身以外数组的乘积

和为 K 的子数组

和可被 K 整除的子数组

连续数组

矩阵区域和

最大子矩阵


前缀和

DP34 【模板】前缀和

分析:dp和arr的大小并不是固定的,就是有没有偏移量,这里的n是从1开始,不如直接放到下标1处,在最后的减法时,如果用第一个参考代码会下标越界到-1,所以说这里的方法并不是固定的,思路,偏移量理清了静下心就可以上手写

注意:a[i] 的最大值是INT_MAX,所以创建long long 类型的dp表

参考代码:dp[n + 1],arr[n + 1]

#include <iostream>
using namespace std;
#include <vector>
int main() {
    int n = 0, q = 0;
    cin >> n >> q;
    vector<int> arr(n + 1);
    for(int i = 1; i < n + 1; i++)
        cin >> arr[i];
    vector<long long> dp(n + 1);
    for(int i = 1; i < n + 1; i++)
        dp[i] = dp[i - 1] + arr[i];
    int l = 0, r = 0;
    while(q--)
    {
        cin >> l >> r;
        cout << dp[r] - dp[l - 1] << endl;
    }
    return 0;
}
// 64 位输出请用 printf("%lld")

参考代码: dp[n],arr[n]

#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);
    dp[0] = arr[0];
    for(int i = 1; i < n; i++)
        dp[i] = dp[i - 1] + arr[i];
    
    int l = 0, r = 0;
    while(q--)
    {
        cin >> l >> r;
        cout << dp[r - 1] - dp[l - 1] + arr[l - 1] << endl;
    }
    return 0;
}
// 64 位输出请用 printf("%lld")

参考代码:dp[n],arr[n + 1]

#include <iostream>
using namespace std;
#include <vector>
int main() {
    int n = 0, q = 0;
    cin >> n >> q;
    vector<int> arr(n + 1);
    for(int i = 1; i <= n; i++)
        cin >> arr[i];
    vector<long long> dp(n);
    dp[0] = arr[1];
    for(int i = 1; i < n; i++)
        dp[i] = dp[i - 1] + arr[i + 1];
    
    int l = 0, r = 0;
    while(q--)
    {
        cin >> l >> r;
        cout << dp[r - 1] - dp[l - 1] + arr[l] << endl;
    }
    return 0;
}
// 64 位输出请用 printf("%lld")

二维前缀和

DP35 【模板】二维前缀和

 

dp和原数组对齐了,就没有偏移量

注意:a[i][j] 的最大值是INT_MAX,所以创建long long 类型的dp表

参考代码

#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 + 1; i++)
        for(int j = 1; j < m + 1; j++)
            cin >> arr[i][j];
    vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
    for(int i = 1; i < n + 1; i++)
        for(int j = 1; j < m + 1; j++)
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - 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[x2][y1 - 1] - dp[x1 - 1][y2] + dp[x1 - 1][y1 - 1] << endl; 
    }
    return 0;
}

寻找数组的中心下标

724. 寻找数组的中心下标

 要点:f[i] = f[i - 1] + nums[i - 1];就是看后面的nums,如果是i - 1,就是不包括自己的前缀和f[0]自然是0,这题包不包括都可以,包括就是等式两边同时加上自己这个元素,

参考代码

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 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;
    }
};

除自身以外数组的乘积

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

注意点:f[0] = 1, g[n - 2] = 1

参考代码

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n), g(n), ret(n);
        f[0] = 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++)
            ret[i] = f[i] * g[i];
        return ret;
    }
};

和为 K 的子数组

560. 和为 K 的子数组

可以不用前缀和,因为这里不需要用到下标随机访问,这里只需要顺序访问

这里不可以用滑动窗口:因为没有单调性

把有的sum都放到哈希表里就行,先判断 : hash.count(sum)   再放入哈希表

注意:这里有个hash[0] = 1,sum = k时候也是符合条件,但是哈希表里没有hash[0],(但是之后是会可能会添加上hash[0],就是中间有个地方前缀和为0),总的来说就是少了一个起点0

参考代码

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        // int n = nums.size(), ret = 0;
        // vector<int> dp(n);
        // dp[0] = nums[0];
        // for(int i = 1; i < n; i++)
        //     dp[i] = dp[i - 1] + nums[i];
        
        // unordered_map<int, int> hash;
        // hash[0] = 1;
        // for(auto e : dp)
        // {
        //     if(hash.count(e - k)) ret += hash[e - k];//hash[e - k]
        //     hash[e]++;
        // }
        // // for(int i = 0; i < n; i++)
        // // {
        // //     if(hash.count(dp[i] - k)) ret += hash(dp[i] - k);
        // //     hash[dp[i]]++;
        // // }
        // return ret;

        unordered_map<int, int> hash;
        hash[0] = 1;
        int sum = 0, ret = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            sum += nums[i];
            if(hash.count(sum - k)) ret += hash[sum - k];
            hash[sum]++;
        }
        return ret;
    }
};

和可被 K 整除的子数组

974. 和可被 K 整除的子数组

参考代码

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) {
        unordered_map<int, int> hash;
        hash[0] = 1;
        int sum = 0, ret = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            sum += nums[i];
            if(hash.count((sum % k + k) % k)) ret += hash[(sum % k + k) % k];
            hash[(sum % k + k) % k]++;
        }
        return ret;
    }
};

连续数组

525. 连续数组

题解:把0 改成 -1,之后求和为0的最长子数组,有负数,自然不能用滑动窗口

因为求的是长度吗,那么这里就是最短索引和前缀和的映射,<前缀和,最小索引>

因为是顺序遍历 ,那么不存在就添加进哈希表,这样就可以得到最小索引,既可以不用dp表,也直接找到最小索引

注意:这样就可以理解为什么是i - hash[sum]

参考代码

class Solution 
{
public:
    int findMaxLength(vector<int>& nums) 
    {
        unordered_map<int, int> hash;
        int sum = 0, ret = 0;
        hash[0] = -1;
        // for(auto e : nums)
        // {
        //     sum += e == 0 ? -1 : e;
        //     if(hash.count(sum)) ret = max(ret, );
        //     else hash[sum]++;
        // }
        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;
    }
}; 

矩阵区域和

1314. 矩阵区域和

分析:涉及偏移量,和对越界的处理,如果二维dp不是 行列 + 1那么边界情况就麻烦了,一维dp若不是n + 1 只有开头或者结尾处理,

偏移:mat 到dp 的偏移,dp 到 ret的偏移

注意:int x2 = min(i + k, m - 1) + 1, y2 = min(j + k, n - 1) + 1;我在写的时候错在不是m - 1

参考代码

class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
        int m = mat.size(), n = mat[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        for(int i = 1; i < m + 1; i++)
            for(int j = 1; j < n + 1; 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++)
            {
                int x1 = max(i - k, 0) + 1, y1 = max(j - k, 0) + 1;
                int x2 = min(i + k, m - 1) + 1, y2 = min(j + k, n - 1) + 1;
                ret[i][j] = dp[x2][y2] - dp[x2][y1 - 1] - dp[x1 - 1][y2] + dp[x1 - 1][y1 - 1];
            }
        return ret;
    }
};

最大子矩阵

最大子矩阵_牛客题霸_牛客网

 出现的问题

  • ret没有初始化为INT_MIN,这个在<climits>里
  • ret的式子里dp[i - 1][x]这个横纵写反了
#include <vector>
#include <iostream>
#include <climits>
using namespace std;

int main() //二维前缀和
{
	int n;
	cin >> n;
	vector<vector<int>> arr(n, vector<int>(n));
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> arr[i][j];
	vector<vector<int>> dp(n + 1, vector<int>(n + 1));
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + arr[i - 1][j - 1];
	int ret = INT_MIN; // 可能都是负值。。。
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			for (int y = i; y <= n; y++)
				for (int x = j; x <= n; x++)
					ret = max(ret, dp[y][x] - dp[y][j - 1] - dp[i - 1][x] + dp[i - 1][j - 1]);
					//dp[i - 1][x]这里写反了
	cout << ret;
}

  • 35
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值