题单名称
【算法1-6】二分查找与二分答案
【深基13.例1】查找
题目描述
输入 n ( n ≤ 1 0 6 ) n(n\le10^6) n(n≤106) 个不超过 1 0 9 10^9 109 的单调不减的(就是后面的数字不小于前面的数字)非负整数 a 1 , a 2 , … , a n a_1,a_2,\dots,a_{n} a1,a2,…,an,然后进行 m ( m ≤ 1 0 5 ) m(m\le10^5) m(m≤105) 次询问。对于每次询问,给出一个整数 q ( q ≤ 1 0 9 ) q(q\le10^9) q(q≤109),要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 -1 。
输入格式
第一行 2 个整数 n 和 m,表示数字个数和询问次数。
第二行 n 个整数,表示这些待查询的数字。
第三行 m 个整数,表示询问这些数字的编号,从 1 开始编号。
输出格式
m 个整数表示答案。
样例 #1
样例输入 #1
11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6
样例输出 #1
1 2 -1
提示
1 0 6 10^6 106 规模的数据读入,请用 scanf。用 cin 会超时。
思路
这里需要注意,在二分的过程中要寻找到一个确定数,未找到则返回-1
因为外层while循环条件为lo < hi,如果lo == hi则跳出循环
此外查找的数必须是第一次出现的位置,因此如果mid合法,就需要向左收缩区间,即hi = mid
这两点考虑清楚二分的函数就很好写了
代码
// P2249 【深基13.例1】查找
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[1000010];
int q[100010];
int search_binary(int key, int a[], int lo, int hi){
//key为查找数,a为查找数组,lo-hi为查找区间下标
//返回key在a数组的[lo,hi]区间中第一次出现的下标值+1(即位置),未查找到则返回-1
while(lo < hi){//当hi == lo时跳出
int mid = (hi- lo)/2 + lo;
if (key < a[mid]) hi = mid-1; //向左收缩区间
else if (key > a[mid]) lo = mid+1; //向右收缩区间
else hi = mid; //向左收缩区间,但保留当前数
}
if(a[hi] == key) return hi+1;
else return -1;
}
int main(){
int n, m;
cin >> n >> m;
for(int i=0; i<n; i++){
scanf("%d", &a[i]);
}
for(int i=0; i<m; i++){
scanf("%d", &q[i]);
cout << search_binary(q[i], a, 0, n-1) << " ";
}
return 0;
}
A-B 数对
题目描述
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!
好吧,题目是这样的:给出一串数以及一个数字 C C C,要求计算出所有 A − B = C A - B = C A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个整数 N , C N, C N,C。
第二行, N N N 个整数,作为要求处理的那串数。
输出格式
一行,表示该串数中包含的满足 A − B = C A - B = C A−B=C 的数对的个数。
样例 #1
样例输入 #1
4 1
1 1 2 3
样例输出 #1
3
提示
对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1≤N≤2000。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1 \leq N \leq 2 \times 10^5 1≤N≤2×105。
保证所有输入数据绝对值小于 2 30 2^{30} 230,且 C ≥ 1 C \ge 1 C≥1。
2017/4/29 新添数据两组
思路
一开始想用hash数组,但是数字大小为2e30,开这么大的数组不现实,因为实际输入的个数只有2e5,因此利用map映射,存储每一个数字出现的位置更加合理
然后将问题转化为迭代map容器,求所有键大小差c的pair对的值的乘积,并且将所有可能的乘积加和返回即可。
代码
// P1102 A-B 数对
// map映射
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
int main(){
map<ll, ll> m;
ll n, c;
cin >> n >> c;
for(ll i=0; i<n; i++){
ll temp;
cin >> temp;
m[temp]++;
}
// fflush(stdin);
ll cnt = 0;
for (auto iter = m.begin(); iter!=m.end(); ++iter) {
if(iter->second)
cnt += iter->second * m[iter->first+c];
}
cout << cnt;
}
[COCI 2011/2012 #5] EKO / 砍树
题目描述
伐木工人 Mirko 需要砍 M M M 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。
Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 H H H(米),伐木机升起一个巨大的锯片到高度 H H H,并锯掉所有树比 H H H 高的部分(当然,树木不高于 H H H 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 20 , 15 , 10 20,15,10 20,15,10 和 17 17 17,Mirko 把锯片升到 15 15 15 米的高度,切割后树木剩下的高度将是 15 , 15 , 10 15,15,10 15,15,10 和 15 15 15,而 Mirko 将从第 1 1 1 棵树得到 5 5 5 米,从第 4 4 4 棵树得到 2 2 2 米,共得到 7 7 7 米木材。
Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 H H H,使得他能得到的木材至少为 M M M 米。换句话说,如果再升高 1 1 1 米,他将得不到 M M M 米木材。
输入格式
第 1 1 1 行 2 2 2 个整数 N N N 和 M M M, N N N 表示树木的数量, M M M 表示需要的木材总长度。
第 2 2 2 行 N N N 个整数表示每棵树的高度。
输出格式
1 1 1 个整数,表示锯片的最高高度。
样例 #1
样例输入 #1
4 7
20 15 10 17
样例输出 #1
15
样例 #2
样例输入 #2
5 20
4 42 40 26 46
样例输出 #2
36
提示
对于 100 % 100\% 100% 的测试数据, 1 ≤ N ≤ 1 0 6 1\le N\le10^6 1≤N≤106, 1 ≤ M ≤ 2 × 1 0 9 1\le M\le2\times10^9 1≤M≤2×109,树的高度 < 1 0 9 <10^9 <109,所有树的高度总和 > M >M >M。
思路
因为随着刀片高度的改变,切割的木料米数是单调变化的,并且一定不会出现重复值,因此我们可以使用二分的方法,找到最合适的刀片大小,注意这里要找到最大的合法刀片高度,以节省损耗
代码
// P1873 [COCI 2011/2012 #5] EKO / 砍树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(int argc, char const *argv[])
{
int n, m;
int high[1000010];
ll sum = 0;
int maxhigh = 0, minhigh = 0;
//读数据,查找最大值maxhigh和最小值minhigh
cin >> n >> m;
for(int i=0; i<n; i++){
scanf("%d", &high[i]);
}
minhigh = high[0];
for(int i=0; i<n; i++){
minhigh = min(minhigh, high[i]);
maxhigh = max(maxhigh, high[i]);
}
int lo = minhigh, hi = maxhigh;
//二分,区间为0-maxhigh
while(lo <= hi){
ll mid = (hi - lo)/2 + lo;
//计算mid高度下获取的木材长度
sum = 0;
for(int i=0; i<n; i++){
if(high[i] > mid) sum += high[i] - mid;
}
if(sum >= m) lo = mid+1;//区间右缩
else hi = mid-1;//区间左缩
}
//打印输出
cout << hi;
return 0;
}