题目描述
题目描述:
又到了丰收的季节,恰逢小易去牛牛的果园里游玩。
牛牛常说他对整个果园的每个地方都了如指掌,小易不太相信,所以他想考考牛牛。
在果园里有N堆苹果,每堆苹果的数量为ai,小易希望知道从左往右数第x个苹果是属于哪一堆的。
牛牛觉得这个问题太简单,所以希望你来替他回答。
输入描述:
第一行一个数n(1 <= n <= 105)。
第二行n个数ai(1 <= ai <= 1000),表示从左往右数第i堆有多少苹果
第三行一个数m(1 <= m <= 105),表示有m次询问。
第四行m个数qi,表示小易希望知道第qi个苹果属于哪一堆。
输出描述:
m行,第i行输出第qi个苹果属于哪一堆。
时间限制:1秒 空间限制:262144K
题目分析
首先给出下文要用到的符号与其释义。
符号 | 值 | 符号的意义 |
---|---|---|
ai | / | 第i个i苹果堆拥有的苹果个数 |
bi | ∑(ai) | 到第i个苹果堆为止累计的苹果数量 |
qi | / | 第i次询问的苹果编号 |
si | / | 第i次询问的答案 |
题目明面上考的区分第qi个苹果属于哪一堆,由于苹果的编号具有自增性,其编号为当前堆的编号加上之前苹果堆的个数。因此题目询问的是某个正整数在等差增数列中哪一个区域。因此,第i个苹果在第k个苹果堆时,具有以下性质。
- bsi-1<qi ≦bsi
由于以上性质,所以题目等价于查找增数列中恰好大于或等于某个正整数的项数。又由于数列是有序增数列,因此可以采用二分查找。此时,算法的时间复杂度仅为O(n*long n)。
解决方案
首先,创建数列bn。根据 bi=∑(ai),很容易得到一个时间复杂度为O(n)的算法。
int n;
cin>>n;
int sum=0,*a;
int* a=new int[n]
for(int i=0;i<n;++i)
{
cin>>*(a+i);
*(a+i)+=sum;
sum=*(a+i);
}
对于输出答案只需要调用编写好的递归函数即可。
for(int i=0;i<m;++i)
{
cin>>q;
ans=bisearch(a,0,n-1,q);
cout<<ans<<'\n';
}
接下来重点来了,实现上述算法所要求的二分算法。
int bisearch(int* a,int begin,int end,int q)
{
if(q<*(a+begin))
{
return being+1;
}
if(q>*(a+end-1))
{
return end+1;
}
int mid=(begin+end)/2;
if(q<*(a+mid-1))
{
return bisearch(a+1,begin,mid,q);
}
else if(q>*(a+mid))
{
return bisearch(a,mid,end-1,q);
}
else
{
return mid+1;
}
}
二分查找是一个很优秀的查找算法,时间复杂度仅为O(log n),不过依旧许多优化空间,比如我用的增小缩量就是其中之一。
明天的题
又到了周末,小易的房间乱得一团糟。
他希望将地上的杂物稍微整理下,使每团杂物看起来都紧凑一些,没有那么乱。
地上一共有n团杂物,每团杂物都包含4个物品。第i物品的坐标用(ai,bi)表示,小易每次都可以将它绕着(xi,yi)逆时针旋转90°,这将消耗他的一次移动次数。如果一团杂物的4个点构成了一个面积不为0的正方形,我们说它是紧凑的。
因为小易很懒,所以他希望你帮助他计算一下每团杂物最少需要多少步移动能使它变得紧凑。