目录
例1
例2
例3
例4
例5
例6
例7
例8
例1
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")
例2
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;
}
例3
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;
}
};
例4
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;
}
};
例5
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;
}
};
例6
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;
}
};
例7
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;
}
};
例8
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;
}
};