洛谷题单——【算法1-6】二分查找与二分答案

vAHwSP.jpg
但行好事,莫问前程。



题单名称

【算法1-6】二分查找与二分答案

【深基13.例1】查找

题目描述

输入 n ( n ≤ 1 0 6 ) n(n\le10^6) n(n106) 个不超过 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(m105) 次询问。对于每次询问,给出一个整数 q ( q ≤ 1 0 9 ) q(q\le10^9) q(q109),要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 -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 AB=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个整数 N , C N, C N,C

第二行, N N N 个整数,作为要求处理的那串数。

输出格式

一行,表示该串数中包含的满足 A − B = C A - B = C AB=C 的数对的个数。

样例 #1

样例输入 #1

4 1
1 1 2 3

样例输出 #1

3

提示

对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1N2000

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1 \leq N \leq 2 \times 10^5 1N2×105

保证所有输入数据绝对值小于 2 30 2^{30} 230,且 C ≥ 1 C \ge 1 C1

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 1N106 1 ≤ M ≤ 2 × 1 0 9 1\le M\le2\times10^9 1M2×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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

introversi0n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值