前缀和数组是十分基本和简单的一种工具,但是要想真正用好,却不容易。在平时的训练中有很多的问题都要用到前缀和。我们通过一个小的例子来看一下
cogs1190最大和
题目大意:N个数围成一圈,要求从中选择若干个连续的数(注意每个数最多只能选一次)加起来,问能形成的最大的和。
思路:一看到这个题,竟然想到了线段树,后来发现有点大材小用,要是深究的话也不是很会写,于是就另辟蹊径。这个题目中有一个神奇的关系就是这是个环,于是我们就有了两种情况:1)这一段数本身就在给定区间内;2)这一段数被分放在给定区间的两端。第一种情况比较好处理,只用前缀和,跟新最小值和最大的差就可以了;后一种情况就要用到后缀和,找到最大值和后缀和的和取最大。输出最大值就可以了。
#include<iostream> #include<cstdio> using namespace std; int sum1[100001]={0},sum2[100001]={0},a[100001]={0}; int main() { freopen("maxsum.in","r",stdin); freopen("maxsum.out","w",stdout); int n,i,j,maxn,minn; maxn=-2100000000; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d",&a[i]); sum1[i]=sum1[i-1]+a[i]; } for (i=n;i>=1;--i) sum2[i]=sum2[i+1]+a[i]; minn=2100000000; for (i=1;i<=n;++i) { if (sum1[i]-minn>maxn) maxn=sum1[i]-minn; if (sum1[i]<minn) minn=sum1[i]; } minn=-2100000000; for (i=1;i<=n;++i) { if (minn+sum2[i]>maxn) maxn=minn+sum2[i]; if (sum1[i]>minn) minn=sum1[i]; } printf("%d\n",maxn); fclose(stdin); fclose(stdout); }
这样一道题目中展现了前缀和的特殊魅力,以后还要多加练习,熟练应用。
现在才知道,这个二级前缀和数组好像是一种高端的东西,差分序列。
cogs1435金发姑娘和N头牛
题目大意:取一种温度,让牛能产出最多的奶。已知每头牛最适合的温度和在这个温度区间上中下不同的产奶量。
思路:先离散化,然后扫一遍所有的ai、bi,然后在0、ai、bi+1(一开始写成了bi,竟然过了75分。。。)处加上相应的值、在ai、bi+1、maxn减去相应的值,最后从头到尾扫一遍前缀和数组,最大值就是答案了。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int a[20001]={0},b[20001]={0},cc[80001]={0},sum[100000]={0},dd[100000]={0}; int main() { freopen("milktemp.in","r",stdin); freopen("milktemp.out","w",stdout); int maxn=0,n,i,j,x,y,z,tot=0,size,ans=0; scanf("%d%d%d%d",&n,&x,&y,&z); for (i=1;i<=n;++i) { scanf("%d%d",&a[i],&b[i]); ++tot;cc[tot]=a[i]; ++tot;cc[tot]=b[i]; } sort(cc+1,cc+tot+1); size=unique(cc+1,cc+tot+1)-cc-1; for (i=1;i<=n;++i) { a[i]=upper_bound(cc+1,cc+size+1,a[i])-cc-1; b[i]=upper_bound(cc+1,cc+size+1,b[i])-cc-1; maxn=max(maxn,max(a[i],b[i])); } maxn+=2; for (i=1;i<=n;++i) { dd[0]+=x; dd[a[i]]-=x;dd[a[i]]+=y; dd[b[i]+1]-=y;dd[b[i]+1]+=z; dd[maxn]-=z; } ans=sum[0]=dd[0]; for (i=1;i<=maxn;++i) { sum[i]=sum[i-1]+dd[i]; ans=max(ans,sum[i]); } printf("%d\n",ans); fclose(stdin); fclose(stdout); }