倍增,字面意思就是“成倍增长”。这是指我们在进行递推时,如果状态空间很大,
通常的线性递推无法满足时间与空间复杂度的要求,那么我们可以通过成倍增长的方式.只递推状态空间中在2的整数次幂位置上的值作为代表。当需要其他位置上的值时,我们通过“任意整数可以表示成若干个2的次幂项的和”这一性质,使用之前求出的代表值拼成所需的值。所以使用倍增算法也要求我们递推的问题的状态空间关于2的次幂具有可划分性。
“倍增”与“二进制划分”两个思想互相结合,降低了求解很多问题的时间与空间复杂度。我们之前学习的快速幂其实就是“倍增”与“二进制划分”思想的一种体现。在本文中, 我们研究序列上的倍增问题, 包括求解RMQ(区间最值) 问题的ST算法。关于求解最近公共祖先(LCA) 等在树上 的倍增应用, 我们将在以后的博文中进行探讨。
例题:给定一个长度为n的数列a,然后进行若干次访问,每次给定一个整数T,求出最大的k,满足a[1]~a[k]区间和<=T(算法必须在线)。
题解
用倍增的思想。
令 p=1, k = 0, sum = 0;
求出a的前缀和,记在s数组中。如果sum+s[k+p]-s[k]<=T,那么sum+=s[k+p]-s[k], k += p, p*=2 ,否则的话 p /= 2;
重复第二步,直到k=0为止。
就是把序列先加1个,再加2个,再加4个…,k用来记录已经加了几个数了,每次都是再上次k个数再加P个数,当然是s[p+k]-s[k]
代码:
#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 10005
int n,a[maxn],t,s[maxn];
int main()
{
cin>>n>>t;
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
s[i] = s[i-1]+a[i];
}
int p = 1, k = 0, sum = 0;
while(p) {
if(k+p<=n&&sum+s[k+p]-s[k]<=t) {
sum += s[k+p]-s[k];
k+=p;
p*=2;
}else {
p /= 2;
}
}
cout<<k<<endl;
return 0;
}