目录
1.前缀和定义主要内容
通过创建一个新的数组预处理数组从起始到各位置的元素累加和得前缀和数组,再用其元素相减快速获指定区间元素和,从而避免重复遍历降低复杂度,达到O(1)实现。进阶的话主要就是对于数组区间的处理思想。
2.模型构造方式
for(int i=1;i<=n;i++)
{
prefix[i]=prefix[i-1]+arr[i];
}
//一般方便满足构造初始化prefix[1]以及后面索引不混乱 原数组和前缀和数组都从index=1开始
3.蓝桥云课经典例题分享
1.<小郑的蓝桥平衡串>
1.题目
2.思路分析
子串问题通常就是双指针或者前缀和等处理,有关区间和的问题想到用前缀和,那就把L和Q转化成1和-1,实现区间和来判断是否为平衡串
3.代码实现
#include<bits/stdc++.h>
const int N=1010;
using namespace std;
long long prefix[N];
int main(){
string s;
cin>>s;
for(int i=0;i<s.length();i++) prefix[i]=prefix[i-1]+(s[i]=='L'?1:-1);
int ans=0;
for(int i=0;i<s.length();i++)
for(int j=i+1;j<s.length();j++)
if(prefix[j]-prefix[i-1]==0) ans=max(ans,j-i+1);
//双循环枚举每一个子串 不断更新找最长字串
cout<<ans<<" ";
return 0;
}
2.<最大数组和>
1.题目
(样例输入)
6
5 1
2 5 1 10 6
5 2
2 5 1 10 6
3 1
1 2 3
6 1
15 22 12 10 13 11
6 2
15 22 12 10 13 11
5 1
999999996 999999999 999999997 999999998 999999995
(样例输出)
21
11
3
62
46
3999999986
2.思路分析
其实最简单的关于前缀和的区间和用accumulate函数也可以快速实现,这道题的踩坑点在于不能用贪心的思想,每一步局部最优选择不一定能够实现整体最优,所以一开始样例点只能通过50%
1
6 2
15 22 12 10 13 11
就比如说这个样例,用贪心想的话先去掉后来两个这样就不会去掉第二个最大值因为k=2只能操作两次,正确的应该使去掉两个最大值,因为前两个比后四个小,要整体考虑。因此再次需要用循环枚举出所有k次操作的情况
3.代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+7;
ll a[N];
ll res=0;
int main()
{
ll t;cin>>t;
while(t--)
{
ll n,k;cin>>n>>k;
for(ll i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
for(ll i=1;i<=n;i++) a[i]+=a[i-1];//把a[i]转化为前缀和数组
for(ll i=0;i<=k;i++) //枚举假设删去i次最大值
{
ll minnumber= (k-i)*2;
ll maxnumber=i;
if(minnumber+maxnumber<=n)//对于数组遍历的前提一定是不能够数组越界
{
ll now=a[n-maxnumber]-a[minnumber];
res=max(res,now);
}
}
cout<<res<<'\n';
res=0;
}
return 0;
}
3.<大石头的搬运工>
1.题目
2.思路分析
这道题就很考验思维了,不能够无脑套前缀和的模板。怎么能够想到前缀和呢?根据题意,需要做的是把n堆石头搬到同一处,就是搬到中间某一处(包括原来某一个石头所在位置),肯定是要枚举找出左右同时过来累加的(重量*路径长度)最小的情况,这种累加的情况正是前缀和的思想,只不过还得有个后缀和哈哈。
同时得由特殊到一般考虑,就是说假如是2堆石头的话,一方重一方轻那么根据数学关系显而易见只需要动轻的到中的这里就可以,重的不需要动(不然枚举的累死了)。如果一样重的话中间哪里都一样啊,加权平均数一样大。
3.代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+9;
ll prefix[N];
ll back[N];
pair<int,int>stones[N];//pair和结构体一样相当于一种数据类型所以有对应的数组
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>stones[i].second>>stones[i].first;
}
sort(stones+1,stones+n+1);//不要忘记初始元素地址
ll weight=0;
for(int i=1;i<=n;i++){
prefix[i]=prefix[i-1]+weight*(stones[i].first-stones[i-1].first);
weight+=stones[i].second;
}
weight=0;
for(int i=n;i>=1;i--){
back[i]=back[i+1]+weight*(stones[i+1].first-stones[i].first);
weight+=stones[i].second;
}
ll ans=1e18;
for(int i=1;i<=n;i++){
ans=min(ans,prefix[i]+back[i]);//更新ans
}
//只遍历n个石头的原始位置是因为贪心思想 特殊情况两个的话最好的方式只能是动轻的那个 如果一样重的话中间无论动到哪里都一样
//特殊到一般的话就相当于左右两边的 因此就想到用前缀和 因为要遍历统计左右两边的 有累加的情况就是前缀和思想
cout<<ans<<endl;
return 0;
}
//在前缀和模板的情况下经常是累加的东西不一样 这就需要根据题意具体的考虑了。
4.总结
本篇主要就是个人对于前缀和的认知和例题题解思路的分享,算是对于自己算法学习的小记录,第一篇完结撒花!!!