目录
介绍
【模版】一维前缀和
算法思路:
代码实现
【模版】二维前缀和
算法思路
代码实现
寻找数组中心的下标
算法思路
代码实现
总结
除自身以外数组的乘积
算法思路
代码实现
和为K的子数组
算法思路
代码实现
和可被整除的K的子数组
算法思想
代码实现
连续数组
算法思想
代码实现
总结
矩阵区域和
算法思想
代码实现
总结
介绍
前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。
【模版】一维前缀和
模版前缀和【模板】前缀和_牛客题霸_牛客网
算法思路:
代码实现
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;cin>>n;
vector<int>s(n+1);
int q=0;cin>>q;
for(int i=1;i<=n;i++)cin>>s[i];
vector<long long>dp(n+1);//防止数据溢出
for(int i=0;i<=n;i++)
dp[i]=dp[i-1]+s[i];
while(q--)
{
int r=0,l=0;
cin>>r>>l;
cout<<dp[l]-dp[r-1]<<endl;
}
return 0;
}
【模版】二维前缀和
【模版二维前缀和】【模板】二维前缀和_牛客题霸_牛客网
算法思路
代码实现
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m,q;
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];
//预处理二维矩阵
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-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1];
while(q--)
{
int x1,y1,x2,y2;
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;
}
寻找数组中心的下标
寻找数组中心的下标. - 力扣(LeetCode)
算法思路
代码实现
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;
}
};
总结
此类方法命名为前缀和,并非仅仅是“前”缀和,也有可能是后缀和,与其将其看作是模版,不如将其理解为一种思想,灵活运用。
除自身以外数组的乘积
除自身以外数组的乘积. - 力扣(LeetCode)
算法思路
代码实现
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n=nums.size();
vector<int>f(n,1),g(n,1);
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];
vector<int>ans;
for(int i=0;i<n;i++)
{
ans.push_back(f[i]*g[i]);
}
return ans;
}
};
和为K的子数组
和为K的子数组. - 力扣(LeetCode)
算法思路
代码实现
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
map<int,int>hash;
int sum=0;
int ret=0;hash[0]=1;
for(auto x:nums)
{
sum+=x;//求出前缀和
if(hash[sum-k])ret+=hash[sum-k];//每次sum都是已知量,又推出的关系我们可以确定每次前一部分的序列也是已知量;
hash[sum]++;
}
return ret;
}
};
和可被整除的K的子数组
和可被整除的K的子数组. - 力扣(LeetCode)
算法思想
代码实现
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k) {
int ret=0;
map<int,int>hash;
hash[0]=1;
int sum=0;
for(auto n:nums)
{
sum+=n;
int r=(sum%k+k)%k;
if(hash.count(r))ret+=hash[r];
hash[r]++;
}
return ret;
}
};
连续数组
连续数组. - 力扣(LeetCode)
算法思想
代码实现
class Solution {
public:
int findMaxLength(vector<int>& nums) {
int n=nums.size();
int sum=0;
map<int,int>hash;
hash[0]=-1;
int 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;
}
};
总结
上面的这4道题,又很高的相似性,都是在一段序列中,后一部分是连续的,而前面的一部分与整个序列的和具有一定的关系,每次我们求出的整个序列的sum都是已知的,通过退出的前一段序列的前缀和与整个数组的前缀和关系,可以得到前一段序列的前缀和,比如sum-k,sum%k...我们只需要每次查找有多少个满足条件的子序列即可;
矩阵区域和
矩阵区域和. - 力扣(LeetCode)
算法思想
代码实现
class Solution {
public:
vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {
int m=mat.size();//行
int n=mat[0].size();//列
vector<vector<int>>dp(m+1,vector<int>(n+1));//数组映射
//预处理二维矩阵
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;
int y1=max(0,j-k)+1;
int x2=min(m-1,i+k)+1;
int y2=min(n-1,j+k)+1;
ret[i][j]=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1];//在dp矩阵中计算
}
}
return ret;
}
};
总结
二维矩阵和的索引必须是从{1,1,}开始,平时遇到的题中有时候可能会从{0,0}开始,这时候我们就通过映射进行转换,然后使用模版化的二维矩阵进行处理数据;
这道题中处理越界问题的操作值得借鉴,通过max和min函数来更加方便的进行下标的处理;
另外就是二维模版的公式,不需要死记硬背,每次使用时只需要随手画一个图就能轻松写出;