倍增初学有感

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;
} 

猜你喜欢

转载自blog.csdn.net/cqbz_lipengcheng/article/details/107330388