1.总概:
倍增是指当普通的线性枚举无法满足时间与空间要求,我们可以利用任意整数都可以表达成2的次幂项的和这样的特性,从而只去枚举2的整数次幂上的和作为代表,其他的用代表值拼出即可。
总的来说就是类似于二分的优化算法。
2.题目:
给定一个长度为 N 的数列 A ,然后进行若干次查询 , 每一次给定一个整数 T , 求出最大的 k , 满足
且算法必须是在线的(每给一次询问,就给出结果) ;
思路:
1.朴素算法从前往后依次找,时间为O(n)
2.二分:定义一个s数组为a数组的前缀和数组,对k进行二分,如果不算处理时间那么时间为O(log n),这个算法虽然时间较小但是遇到k为1这样的特殊数据,时间会比朴素算法慢。
3.倍增:
(1).定义 p = 1(每次倍增的变量), k = 0(目标值), sum = 0(现k个数之和)。
(2).开始倍增,首先判断若把k向后移动p位是否成立,若可以择将k+=p,p增大2倍, sum+=s[k + p] - s[k]。若不可以则将p缩小2倍。最后重复操作直至p = 0;
代码:
朴素:
#include <iostream>
#include <cstdio>
using namespace std;
int n, a[1005], sum = 0, t, k;
int main() {
scanf("%d %d", &n, &t);
for(int i = 1;i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1;i <= n; i++) {
if(sum + a[i] <= t) {
sum+=a[i];
k++;
}
}
printf("%d", k);
return 0;
}
二分:
#include <iostream>
#include <cstdio>
using namespace std;
int n, a[1005], sum = 0, t, k, s[1005];
int main() {
scanf("%d %d", &n, &t);
for(int i = 1;i <= n; i++) {
scanf("%d", &a[i]);
s[i] = s[i - 1] + a[i];
}
int qi = 0, zhong = n, mid;
while(qi <= zhong) {
mid = (qi + zhong) / 2;
if(s[mid] <= t) {
qi = mid + 1;
}
else{
zhong = mid - 1;
}
}
printf("%d", mid);
return 0;
}
倍增:
#include <iostream>
#include <cstdio>
using namespace std;
int n, a[1005], sum = 0, t, k, s[1005];
int main() {
scanf("%d %d", &n, &t);
for(int i = 1;i <= n; i++) {
scanf("%d", &a[i]);
s[i] = s[i - 1] + a[i];
}
int p = 1;
while(p != 0) {
if(sum + s[k + p] - s[k] <= t) {
k+=p;
sum = sum + s[k + p] - s[k];
p*=2;
if(p * 2 >= n) {
p /= 2;
}
}
else{
p/=2;
}
}
printf("%d", k);
return 0;
}