又到了丰收的季节,恰逢小易去牛牛的果园里游玩。
牛牛常说他对整个果园的每个地方都了如指掌,小易不太相信,所以他想考考牛牛。
在果园里有N堆苹果,每堆苹果的数量为ai,小易希望知道从左往右数第x个苹果是属于哪一堆的。
牛牛觉得这个问题太简单,所以希望你来替他回答。
第一行一个数n(1 <= n <= 105)。
第二行n个数ai(1 <= ai <= 1000),表示从左往右数第i堆有多少苹果
第三行一个数m(1 <= m <= 105),表示有m次询问。
第四行m个数qi,表示小易希望知道第qi个苹果属于哪一堆。
m行,第i行输出第qi个苹果属于哪一堆。
5 2 7 3 4 9 3 1 25 11
1 5 3
#include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { int n; cin >> n; vector<pair<int, int>>appsum(n + 1, { 0,0 }); for (int i = 1; i <= n; ++i) { cin>>appsum[i].first; appsum[i] = { appsum[i - 1].first + appsum[i].first,i }; } int m,q; cin >> m; for (int i = 0; i != m; ++i) { cin >> q; cout<< lower_bound(appsum.begin(), appsum.end(), make_pair(q,0))->second<< endl; } return 0; }
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a(n,0);
for (int i = 0;i != n; ++i)
{
cin >> a[i];
}
int m;
cin >> m;
vector<int> q(m,0);
for (int i = 0; i != m;++i)
{
cin >> q[i];
}
vector<int> sum(n,0);
vector<int> res(m,0);
sum[0] = a[0];
for (int i = 1; i != n;++i)
{
sum[i] = sum[i-1] + a[i];
}
for (int i = 0;i != m; ++i)
{
int fi= 0, la = n-1;
while (fi < la)
{
int mid = (fi + la)>>1;
if (sum[mid] < q[i])
{
fi = mid + 1;
}
else
{
la = mid;
}
}
res[i] = la + 1;
}
for (int i = 0; i != m; ++i)
{
cout << res[i] << endl;
}
return 0;
}
import bisect n = int(input()) ai = list(map(int, input().split())) m = int(input()) qi = list(map(int, input().split())) for i in range(1, len(ai)): ai[i] += ai[i - 1] res = [] for i in qi: res.append(bisect.bisect_left(ai, i) + 1) for i in res: print(i)
一个简单的二分
import java.util.*; public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int a[] = new int[n]; a[0] = sc.nextInt(); for(int i=1;i<n;i++){ a[i] = a[i-1]+sc.nextInt(); } int m = sc.nextInt(); int[] q = new int[m]; for(int i=0;i<m;i++){ q[i] = sc.nextInt(); } for(int i=0;i<m;i++){ int left=0,right=n-1; while(left+1!=right){ int mid = (left+right)>>1; if(q[i]<=a[mid])right=mid; else left=mid; } System.out.println(q[i]>a[left]?right+1:left+1); } } }
n = int(input()) ns = list(map(int, input().split())) m = int(input()) q = list(map(int, input().split())) for i in range(1, n): ns[i] += ns[i-1] for i in q: l, r =0, n-1 while l < r: mid = (l +r) >> 1 if ns[mid] < i: l = mid + 1 else: r = mid print(r + 1)
importjava.util.Scanner; publicclassMain { //这问题提问次数会很多次,如果每次提问都查询肯定会超时,所以用一个数组nx[i]来存放第i个苹果在第几堆就行,时间复杂度O(n+m+sum) publicstaticvoidmain(String[] args) { Scanner sc = newScanner(System.in); intn = sc.nextInt(); int[] nu = newint[n]; intsum = 0; for(inti = 0; i < n; i++) { nu[i] = sc.nextInt(); sum+=nu[i];//一共有多少个苹果 } intm = sc.nextInt(); int[] mu = newint[m]; for(inti = 0; i < m; i++) { mu[i] = sc.nextInt(); } int[] nx = newint[sum+1]; intsu=0; for(inti = 1, j = 0; i <= sum; i++) { if(i<=(nu[j]+su)){ //x=j+1(j从0开始) nx[i]=j+1;//如果第i个苹果小于等于前x推苹果,就赋值为第x推(j从0开始,需加1赋值) }else { nx[i]=++j+1;//否者等于第x+1推苹果,x增加1 su+=nu[j-1];//前x推苹果和 } } for(inti = 0; i < m; i++) { System.out.println(nx[mu[i]]); } }
}
#include <iostream> using namespace std; int which_heap(int* heaps, int num, int length); int main(void){ int n, m, pre = 0, query; cin>>n; int *num = new int[n](); for(int i = 0; i < n; ++i){ cin>>num[i]; num[i] += pre; pre = num[i]; } cin>>m; for(int i = 0; i < m; ++i){ cin>>query; cout<<which_heap(num, query, n)<<endl; } return 0; } int which_heap(int* heap, int num, int length){ int left = 0, right = length-1, mid; while(left+1 < right){ mid = (left+right)/2; if(heap[mid] == num) return mid+1; else if(heap[mid] < num) left = mid; else right = mid; } return (num <= heap[left]) ? left+1 : right+1; }heap[i]表示前i堆之和,这样heap就是一个递增数组,使用二分法,类似二分查找,但此时我们要找的是一个区间而不是数,所以如果while条件是left<right的话可能会变成死循环,我们让right-left=1时退出循环,此时确定区间为[a,b],确定结果就很简单了,当num<=a时,在第left+1堆,否则在第right+1堆
n =int(raw_input().strip()) a =map(int, raw_input().strip().split()) m =int(raw_input().strip()) b =map(int, raw_input().strip().split()) fori inrange(1, n): a[i] +=a[i-1] fori inb: l, r =0, n-1 ans =-1 whilel <=r: mid =(l +r) >> 1 ifa[mid] >=i: ans =mid r =mid -1 else: l =mid +1 printans +1
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); String[] strApple = br.readLine().trim().split(" "); // 求苹果堆的前缀和数组 int[] calSum = new int[n]; for(int i = 0; i < n; i++) { int apple = Integer.parseInt(strApple[i]); if(i == 0) calSum[i] = apple; else calSum[i] = apple + calSum[i - 1]; } int m = Integer.parseInt(br.readLine().trim()); String[] queries = br.readLine().trim().split(" "); for(int i = 0; i < m; i++){ int query = Integer.parseInt(queries[i]); System.out.println(binarySearch(calSum, query) + 1); } } // 找到arr中第一个大于等于num的数的索引 private static int binarySearch(int[] arr, int num) { int left = 0, right = arr.length - 1; while(left < right){ int mid = left + (right - left) / 2; if(arr[mid] < num) left = mid + 1; else right = mid; } return left; } }
def solution(n,m,apples_sum,target): l, r = 0, n-1 while l<r: mid = l+(r-l)//2 if target < apples_sum[mid]: r = mid elif target > apples_sum[mid]: l = mid + 1 else: return mid+1 return r+1 if __name__ == '__main__': n = int(input().strip()) apples = list(map(int, input().strip().split())) m = int(input().strip()) problems = list(map(int, input().strip().split())) for i in range(1, n): # 直接在愿数组上累加求和,这是二分法AC的关键 apples[i] += apples[i - 1] for i in range(m): target = problems[i] print(solution(n=n, m=m, apples_sum=apples, target=target))
前缀和+整数二分
#include <iostream> using namespace std; const int N = 1e5 + 10; int n, m; int a[N], s[N]; int find(int x) { int l = 1, r = n; while(l < r) { int mid = (l + r) >> 1; if(s[mid] >= x) r = mid; else l = mid + 1; } return r; } int main() { cin >> n; for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); s[i] = s[i - 1] + a[i]; } cin >> m; while(m--) { int q; scanf("%d", &q); printf("%d\n", find(q)); } return 0; }
const readline = require('readline') const rl = readline.createInterface({ input: process.stdin, ouput: process.stdout }) let inputArr = [] rl.on('line',line=>{ if(!line) return inputArr.push(line.trim()) if(inputArr.length === 4){ let n = +inputArr[0] let m = +inputArr[2] let appleArr = inputArr[1].split(' ').map(e => +e) // appleArr,表示从左往右数第appleArr[i]堆有多少苹果 -arr let queryArr =inputArr[3].split(' ').map(e => +e) // queryArr,查询第queryArr[i]个苹果的位置 for (let i = 1; i <=n; i++) { appleArr[i] += appleArr[i-1] } queryArr.forEach(i => { let res = search(i,appleArr,0,appleArr.length-1)+1 console.log(res) }) } }) // 有序的二分查找,递归实现 function search(tag, arr, start, end) { if (tag <= arr[0]) { return 0 } let mid = parseInt((start + end) / 2) if (end - start <= 1) { return end } if (tag < arr[mid]) { return search(tag, arr, start, mid) } else if (tag > arr[mid]) { return search(tag, arr, mid, end) } else { return mid } }
#include <bits/stdc++.h> using namespace std; int n,m; int a[100001], s[100001], q[100001]; int BS(int i){ int l=0, r=n-1; while(l<r){ int m = (l+r)>>1; if(s[m]<q[i]) l = m + 1; else r = m; } return r+1; } int main(){ cin>>n; for(int i=0;i<n;i++){ cin>>a[i]; if(i==0) s[i] = a[i]; else s[i] = s[i-1] + a[i]; } cin>>m; for(int i=0;i<m;i++) cin>>q[i]; for(int i=0;i<m;i++) cout<<BS(i)<<endl; return 0; }
import java.util.Scanner; import java.util.Arrays; //菜鸟一个,利用二分查找AC成功,需要注意的是不要导入全部java包,不然会超时。 public class Main{ public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt();//输入苹果堆数 int sum = 0; int[] a = new int[n];//存放每堆苹果数量 for(int i = 0; i < n; i++){ int apple = sc.nextInt(); a[i] = sum + apple;//将每堆苹果以累加的方式存放 形成一个有序不重复数组 sum = a[i]; } int m = sc.nextInt();//输入询问次数 int[] q = new int[m]; for(int i = 0; i < m; i++){ q[i] = sc.nextInt();//存放每次询问苹果的数量 } //利用java的binarySearch(object[ ], object key)方法 //返回值:如果key在数组中,则返回搜索值的索引,从0开始计数; //否则返回-1或者”-“(插入点)。插入点是索引键将要插入数组的那一点,即第一个大于该键的元素索引,从1开始计数。 for(int i = 0; i < m; i++){ int index = Arrays.binarySearch(a, q[i]); if(index > 0){//索引大于0,表示找到相同元素,因为是从0开始,所以加1 System.out.println(index + 1); }else{//索引小于0,就是-1或者”-“(插入点)的情况,直接取反 System.out.println(-index); } } } }
import java.util.Scanner; public class Main{ static class AppleHeap{ int appleNum = 0; int heapNum = 0; AppleHeap(int appleNum, int heapNum){ this.appleNum = appleNum; this.heapNum = heapNum; } } public static void main(String [] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); AppleHeap [] appleHeaps= new AppleHeap[n]; appleHeaps[0] = new AppleHeap(sc.nextInt(),1); for(int i = 1; i < n; i++){ appleHeaps[i] = new AppleHeap(appleHeaps[ i - 1].appleNum + sc.nextInt(),i + 1); } int m = sc.nextInt(); int [] ask = new int[m]; for(int i = 0; i < m; i++){ ask[i] = sc.nextInt(); } for(int i = 0; i < m; i++){ System.out.println(find(appleHeaps, ask[i])); } } //二分查找 private static int find(AppleHeap [] appleHeaps, int num){ int low = 0, high = appleHeaps.length, mid = 0; while(low <= high){ mid = low + (high - low) / 2; if(appleHeaps[mid].appleNum < num){ low = mid + 1; }else if(appleHeaps[mid].appleNum > num){ high = mid - 1; }else { return appleHeaps[mid].heapNum; } } if(appleHeaps[mid].appleNum > num){ return appleHeaps[mid].heapNum; }else { return appleHeaps[mid].heapNum + 1; } } }
/*c语言显然本题直接遍历复杂度达到O(n^2) 对于10^5的数据必然超时采用维护区间和的线段树 用结构体链表(也可以用数组)实现将时间复杂度降低到 建树:O(n) 单点查询:O(logn)拓展:如果用数组实现 空间需要开到4n 证明可以先证明线段树是平衡树(注意:不是查找树)再通过最坏情况是最低一层的节点只有两个 且恰好聚集在最右端 即可证明空间复杂度为O(4n)*/#include <stdio.h>#include <stdlib.h> #include <string.h> #define New(a ,i) (a*)malloc(sizeof(a)*i) #define Cle(a) memset(a ,0 ,sizeof(a)) #define Inv -1 #define MAX 100000 typedef struct node { int num; int key; struct node* le; struct node* ri; }node; node* root; int n=0 ,m=0; int *a=NULL ,*q=NULL; void input() { root=New(node ,1); root->num=root->key=Inv; root->le=root->ri=NULL; scanf("%d" ,&n); a=New(int ,n+1); for(int i=1 ;i<=n ;++i) scanf("%d" ,&a[i]); scanf("%d" ,&m); q=New(int ,m); for(int i=0 ;i<m ;++i) scanf("%d" ,&q[i]); } void bulidtree(int l ,int r ,node** ch) { node* temp=New(node ,1); temp->num=Inv; temp->key=Inv; temp->le=temp->ri=NULL; (*ch)=temp; if(l == r) { (*ch)->num=l; (*ch)->key=a[l]; return; } int mid=(l+r)/2; bulidtree(l ,mid ,&(*ch)->le); bulidtree(mid+1 ,r ,&(*ch)->ri); (*ch)->key=(*ch)->le->key+(*ch)->ri->key; } int search(int key) { node* cur=root; int sum=0; while(1) { if(cur->le && cur->le->key+sum >= key) cur=cur->le; else if(!cur->le) return cur->num; else { sum+=cur->le->key; cur=cur->ri; } } } void ope() { for(int i=0 ;i<m-1 ;++i) printf("%d\n" ,search(q[i])); printf("%d" ,search(q[m-1])); } int main() { input(); bulidtree(1 ,n ,&root); ope(); return 0; }
""" 构建一个前n项求和的序列, 利用bisect找到应该的插入点 """ import sys import bisect if __name__ == "__main__": # sys.stdin = open('input.txt', 'r') n = int(input().strip()) a = list(map(int, input().strip().split())) m = int(input().strip()) q = list(map(int, input().strip().split())) a_sum = [1] for i in range(n): a_sum.append(a_sum[-1] + a[i]) for i in range(m): ans = bisect.bisect(a_sum, q[i]) print(ans)
用treemap复杂度有点高:只能ac40%
import java.util.*;
public class Main {
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
TreeMap<Integer,Integer>map=new TreeMap<>();
int sum=0;
for (int i = 0; i <N; i++)
{
sum=sum+sc.nextInt();
map.put(sum, i+1);
}
int M=sc.nextInt();
for (int i = 0; i <M; i++)
{
Map.Entry<Integer,Integer> index = map.ceilingEntry(sc.nextInt());
if(index != null)
{
System.out.println(index.getValue());
}
else
{
System.out.println(1);
}
}
}