It is lunch time for Mole. His friend, Marmot, prepared him a nice game for lunch.
Marmot brought Mole n ordered piles of worms such that i-th pile contains ai worms. He labeled all these worms with consecutive integers: worms in first pile are labeled with numbers 1 to a1, worms in second pile are labeled with numbers a1 + 1 to a1 + a2 and so on. See the example for a better understanding.
Mole can't eat all the worms (Marmot brought a lot) and, as we all know, Mole is blind, so Marmot tells him the labels of the best juicy worms. Marmot will only give Mole a worm if Mole says correctly in which pile this worm is contained.
Poor Mole asks for your help. For all juicy worms said by Marmot, tell Mole the correct answers.
The first line contains a single integer n (1 ≤ n ≤ 105), the number of piles.
The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 103, a1 + a2 + ... + an ≤ 106), where ai is the number of worms in the i-th pile.
The third line contains single integer m (1 ≤ m ≤ 105), the number of juicy worms said by Marmot.
The fourth line contains m integers q1, q2, ..., qm (1 ≤ qi ≤ a1 + a2 + ... + an), the labels of the juicy worms.
Print m lines to the standard output. The i-th line should contain an integer, representing the number of the pile where the worm labeled with the number qi is.
5 2 7 3 4 9 3 1 25 11
1 5 3
给n个数,其对应一个前缀数组,
再给m个查询,每个查询是一个数,求出该数所在前缀的下标
就是一个裸的二分查找,但是自从田田学长讲过我们的方法是错的以后
一直以来二分都有点不太清楚,所以这里要记录一下
先看代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <numeric>
#include <functional>
#define maxn 100005
using namespace std;
int n;
int m;
int a[maxn];
int binsearch(int tofind)
{
int high=n,low=1;
if(tofind<=a[1]) return 1;
while(1){
int mid=(high+low+1)/2;
if(high==low+1) return high;
else if(a[mid]==tofind) return mid;
else if(a[mid]>tofind){
high=mid;
}
else if(a[mid]<tofind){
low=mid;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i+=1){
cin>>a[i];
if(i>1)
a[i]+=a[i-1];
}
cin>>m;
for(int j=0;j<m;j+=1){
int k;
cin>>k;
cout<<binsearch(k)<<endl;
}
return 0;
}
可以看到与我们以前写的不一样的地方,就是
int mid=(high+low+1)/2;
还有
if(high==low+1) return high;
和
else if(a[mid]>tofind){
high=mid;
}
else if(a[mid]<tofind){
low=mid;
}
也就是,high和low是直接等于mid的,而最关键的还是第一句 mid=(high+low+1)/2;
二分查找无分三种情况
(1)找到a[mid]=tofind;
(2)夹在某三个 a[n+1] a[n] a[n-1] 之间,分别是h,m,l ,接着就会出现在a[n+1] a[n](l=m)或者是在 a[n] a[n-1](h=m)之间的情况,
这个时候因为要求所在前缀,直接取较大的那个就好了
总结一下,如果仅仅是判断某个数组内是否有某个数,那么可以用以前的这种写法:
int binsearch(int tofind)
{
int high=n,low=1;
if(tofind<=a[1]) return 1;
while(1){
int mid=(high+low)/2;
if(high==low) return -1;
else if(a[mid]==tofind) return mid;
else if(a[mid]>tofind){
high=mid-1;
}
else if(a[mid]<tofind){
low=mid+1;
}
}
}
而如果想要求解在哪个前缀中,则应该使用这个写法:
int binsearch(int tofind)
{
int high=n,low=1;
if(tofind<=a[1]) return 1;
while(1){
int mid=(high+low+1)/2;
if(high==low+1) return high;
else if(a[mid]==tofind) return mid;
else if(a[mid]>tofind){
high=mid;
}
else if(a[mid]<tofind){
low=mid;
}
}
}