倍增优化

倍增,字面意思就是“成倍增长”。这是指我们在进行递推时,如果状态空间很大,
通常的线性递推无法满足时间与空间复杂度的要求,那么我们可以通过成倍增长的方式.只递推状态空间中在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 
#include
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;
}

你可能感兴趣的:(动态规划)