[算法上] 2. 二分专题

sort函数

  1. sort 是复合排序(快排为主)
  2. sort (起始地址,结束地址,排序方法cmp);
    结束地址:最后一个元素地址+1
    排序方法cmp:默认是从小到大
  3. 自定义结构必须定义 cmp 或 类内重载<
#include<iostream>
#include <algorithm> //sort
using namespace std;

bool cmp(const int &a, const int &b) {
    return a > b;
}

int main() {
    int num[55] = {9, 2, -5, 5, 7, 22};
    sort(num, num + 6);     //num[0]~num[5]
    sort(num + 1, num + 4); //num[1]~num[3]
    sort(num, num + 6, greater<int>()); //从大到小
    sort(num, num + 6, cmp);            //自定义排序方法cmp
    for (int i = 0; i < 6; i++) {
        cout << num[i] << " ";
    }
    cout << endl;
    return 0;
}

#include<iostream>
#include <string>//string
#include <algorithm>
using namespace std;

struct student {
    int num, age, s;
    string name; //代替char name[20],直接使用 = 赋值
    bool operator< (const student &b) const {
        return this->s > b.s;
    }
};

/*
bool cmp(const student &a, const student &b) {
    if (a.s == b.s) return a.num > b.num;
    return a.s > b.s;
}
*/

int main() {
    student a_202103[205];
    a_202103[0].name = "wu";
    a_202103[0].num = 1;
    a_202103[0].age = 20;
    a_202103[0].s = 60;
    a_202103[1].name = "hu";
    a_202103[1].num = 2;
    a_202103[1].age = 25;
    a_202103[1].s = 150;
    a_202103[2].name = "xu";
    a_202103[2].num = 3;
    a_202103[2].age = 20;
    a_202103[2].s = 60;
    sort(a_202103, a_202103 + 3);
    // sort(a_202103, a_202103 + 3, cmp);
    for (int i = 0; i < 3; i++) {
        cout << a_202103[i].num << " " << a_202103[i].name << " " << a_202103[i].s << endl;
    }
    return 0;
}

struct

c中struct是类型,c++中struct是类,struct的成员的默认访问权限为public,class为private。

381. 谁拿了最多奖学金

  1. c++ 引用的方便

#include <iostream>
#include <algorithm>
#include <string> // c++ string
using namespace std;

struct student {
    string name;
    int num, avg, cla, paper, m; // 对节点进行排序
    char off, west;
};

bool cmp(const student &a, const student &b) {
    if (a.m == b.m) return a.num < b.num;
    return a.m > b.m;
}

student stu[105];
int n, ans;

int func(student &a) {
    a.m = 0;
    if (a.avg > 80 && a.paper >= 1) a.m += 8000;
    if (a.avg > 85 && a.cla > 80) a.m += 4000;
    if (a.avg > 90) a.m += 2000;
    if (a.avg > 85 && a.west == 'Y') a.m += 1000;
    if (a.cla > 80 && a.off == 'Y') a.m += 850;
    return a.m;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> stu[i].name >> stu[i].avg >> stu[i].cla >> stu[i].off >> stu[i].west >> stu[i].paper;
        stu[i].num = i;
        ans += func(stu[i]); // c++引用
    }
    sort(stu, stu + n, cmp);
    cout << stu[0].name << endl << stu[0].m << endl << ans << endl;
    return 0;
}

#include<iostream>
#include <string>
#include <algorithm>
using namespace std;

struct person {
    string name;
    int num, avg, cla, paper, m;
    char off, west; //official干部
};

int n, all;
person stu[105];

bool cmp(const person &a, const person &b) {
    if (a.m == b.m) return a.num < b.num;
    return a.m > b.m;
}

int func(int i) {
    int t = 0;
    if (stu[i].avg > 80 && stu[i].paper >= 1) t += 8000;
    if (stu[i].avg > 85 && stu[i].cla > 80) t += 4000;
    if (stu[i].avg >90) t += 2000;
    if (stu[i].avg > 85 && stu[i].west == 'Y') t += 1000;
    if (stu[i].cla > 80 && stu[i].off == 'Y') t += 850;
    return t;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> stu[i].name >> stu[i].avg >> stu[i].cla >> stu[i].off >> stu[i].west >> stu[i].paper;
        stu[i].num = i; //编号
        stu[i].m = func(i); //奖学金
        all += stu[i].m;
    }
    sort(stu, stu + n, cmp);
    cout << stu[0].name << endl << stu[0].m << endl << all << endl;
    return 0;
}

375. 奖学金

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <algorithm>
using namespace std;

struct student {
    int num, c, m, e, all;
};

bool cmp(const student &a, const student &b) {
    if (a.all == b.all) {
        if (a.c == b.c) return a.num < b.num;
        return a.c > b.c;
    }
    return a.all > b.all;
}

int n;
student stu[305]; // 对象数组

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> stu[i].c >> stu[i].m >> stu[i].e;
        stu[i].all = stu[i].c + stu[i].m + stu[i].e;
        stu[i].num = i;
    }
    sort(stu + 1, stu + n + 1, cmp); // 从1开始存,为了序号num不用+1
    for (int i = 1; i <= 5; i++) {
        cout << stu[i].num << " " << stu[i].all << endl;
    }
    return 0;
}

380. 大统领投票

#include <iostream>
#include <string> //c++ string便于比较(长度/字典序)
#include <algorithm>
using namespace std;

struct node {
    int num;
    string s;
}; //注意分号

int n;
node p[105];

bool cmp(const node &a, const node &b) {
    if (a.s.size() == b.s.size()) {
        return a.s > b.s; //string可以直接比较字典序
    }
    return a.s.size() > b.s.size();
}

int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> p[i].s;
        p[i].num = i; //序号
    }
    sort(p + 1, p + n + 1, cmp);
    cout << p[1].num << endl << p[1].s <<endl;
    return 0;
}

cstring与string

  1. cstring即string.h,是c语言库(.h去掉,前面加c) ,string是c++库
  2. c中字符串本质是字符数组 char s[105],cstring本质上都是对字符数组进行操作,例如strlen(s)会去遍历字符数组,找最后的’\0’
  3. c++中string是类,声明一个对象 string s; s是个对象,是个复合结构,里面有 char[ ]、size_t
  4. c++把c语言中不方便的地方进行了封装
    c中s=“123” 错误
    c++中s=“123” 正确,因为它重载了等号=,再如s=s+“456”

386. 吃瓜群众

#include <iostream>
#include <algorithm>
using namespace std;

struct node {
    int num, ind;
};

node wm[100005];
int n, m;

bool cmp(const node &a, const node &b) {
    return a.num < b.num;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> wm[i].num;
        wm[i].ind = i;
    }
    sort(wm + 1, wm + n + 1, cmp);
    for (int i = 0; i < m; i++) {
        int t, l = 1, r = n;
        cin >> t;
        while (l <= r) {
            int mid = (l + r) / 2; // 越界风险,采用mid=l+(r-l)/2
            if (t == wm[mid].num) {
                cout << wm[mid].ind << endl;
                break;
            }
            if (t > wm[mid].num) l = mid + 1;
            else r = mid - 1;
        }
        if (l > r) cout << 0 << endl; // 出循环不是因为break
    }
    return 0;
}

387. 吃瓜群众升级版

  1. 最大的最小,最长的最短…类似就是二分问题的表述
  2. 找到二分的是什么?下标或答案
  3. 判断0000011111或1111100000的技巧:借助左右两边的小数进行判断

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;

struct node {
    int ind, num;
};

bool cmp(const node &a, const node &b) {
    return a.num < b.num;
}

int n, m;
node wm[100005];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        wm[i].ind  = i;
        scanf("%d", &wm[i].num);
    }
    wm[0].ind = 0, wm[0].num = 2000000000; // 法2:虚拟瓜堆(序号0,数量无限大)
    sort(wm, wm + n + 1, cmp);
    for (int i = 0; i < m; i++) {
        int t, l = 0, r = n;
        scanf("%d", &t); // 法1:与最大瓜堆进行判断
        while (l != r) {
            int mid = (l + r) / 2; // l+(r-l)/2不会越界,或把int变为long long
            if (t > wm[mid].num) l = mid + 1;
            else r = mid;
        }
        printf("%d\n", wm[l].ind);
    }
    return 0;
}

⭐二分的2种特殊情况

  1. 二分的本质:删掉不存在答案的区间
特殊情况1:000000011111
while(l != r){
	int mid = (l + r) / 2;
	if (n[mid] == 0)  l = mid + 1;
	else r = mid;//避免漏解
}

特殊情况2:1111110000000
while(l != r){
	int mid = (l + r + 1) / 2;//+1避免死循环
	if (n[mid] == 1)  l = mid;//避免漏解
	else r = mid - 1;
}

390. 原木切割

  1. 二分【小段木头的长度】,mid是临时的长度,之前二分的都是数组的下标(给数组,在数组中找值)
  2. 随着小段木头的长度的上升,段数下降,具有【单调性】
  3. 4.9能切出8段,5能切出8段,5.1只能切出6段,满足111000特殊情况

#include <iostream>
using namespace std;

int n, m, rmax, num[100005];

int func(int t) {
    int ans = 0;
    for (int i = 0; i < n; i++) ans += num[i] / t;
    return ans;
}

int bs() {
    // 111000 判断是哪种类型可以参考左右两边的小数
    int l = 1, r = rmax;
    while (l != r) {
        int mid = (l + r + 1) >> 1;
        int s = func(mid);
        if (s >= m) l = mid;
        else r = mid - 1;
    }
    return l; // 此时l==r
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> num[i];
        rmax = max(rmax, num[i]);
    }
    cout << bs() << endl;
    return 0;
}

389. 暴躁的程序猿

#include <iostream>
#include <algorithm>
using namespace std;

int n, m, num[100005];

// 最近距离为x时,能安排的人数
int func(int x) {
    int t = 1, last = num[0]; // num[0]位置一定安排人
    for (int i = 1; i < n; i++) {
        if (num[i] - last >= x) {
            t++;
            last = num[i];
        }
    }
    return t;
}

int bs() {
    int l = 1, r = num[n - 1] - num[0];
    while (l != r) {
        int mid = (l + r + 1) >> 1; // mid临时答案
        int s = func(mid); // 数组换函数
        if (s >= m) l = mid;
        else r = mid - 1;
    }
    return l;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> num[i];
    }
    sort(num, num + n);
    cout << bs() << endl;
    return 0;
}

393. 切绳子(小数)

  1. 二分小数【循环条件】
  2. 直接舍掉2位后的小数,输出


#include <iostream>
#include <cstdio> // printf
#include <algorithm>
using namespace std;

int n, m;
double num[10005], tr; // tr右边界

int func(double x) {
    int t = 0;
    for (int i = 0; i < n; i++) {
        t += num[i] / x;
    }
    return t;
}

double bs() {
    double l = 0, r = tr;
    while (r - l > 1e-5) { // 循环条件:大于预设精度
        double mid = (l + r) / 2;
        int s = func(mid);
        if (s >= m) l = mid;
        else r = mid;
    }
    return l;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> num[i];
        tr = max(tr, num[i]);
    }
    sort(num, num + n);
    double t = bs();
    printf("%.2lf\n", (int)(t * 100) / 100.0); // 强转取整直接舍掉小数
    // printf("%.2lf\n", t - 0.005); // printf默认四舍五入
    return 0;
}

82. 伐木

在这里插入图片描述

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <algorithm>
using namespace std;

int n, m, num[1000005], tr;

long long func(int x) {
    long long ans = 0;
    for (int i = 0; i < n; i++) {
        if (num[i] > x) ans += num[i] - x;
    }
    return ans;
}

int bs() {
    int l = 0, r = tr;
    while (l != r) {
        int mid = ((long long)l + r + 1) / 2;
        long long s = func(mid);
        if (s >= m) l = mid;
        else r = mid - 1;
    }
    return l;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> num[i];
        tr = max(tr, num[i]);
    }
    cout << bs() << endl;
    return 0;
}

391. 数列分段

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <algorithm>
using namespace std;

long long n, m, num[100005], l, r;

long long func(long long x) { // 最大和为x时,能将数组分为的段数
    /*
    long long now = 0, t = 0; // now当前和,t段数
    for (int i = 0; i < n; i++) {
        if (now + num[i] > x) {
            t++;
            now = num[i];
        } else if (now + num[i] == x) {
            t++;
            now = 0;
        } else {
            now += num[i];
        }
    }
    if (now != 0) t++; // 等于0是刚好最后一段
    */
    long long now = 0, t = 0;
    for (int i = 0; i < n; i++) {
        if (now + num[i] <= x) { // 合并2种情况
            now += num[i];
        } else {
            t++;
            now = num[i];
        }
    }
    t++; // 最后一段
    return t;
}

int bs() {
    while (l != r) {
        long long mid = (l + r) / 2;
        long long s = func(mid);
        if (s <= m) r = mid;
        else l = mid + 1;
    }
    return l;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> num[i];
        l = max(l, num[i]);
        r += num[i];
    }
    cout << bs() << endl;
    return 0;
}

394. 跳石头

在这里插入图片描述
在这里插入图片描述

#include <iostream>
using namespace std;

int l, r, n, m, num[50005];

// 最近距离为x时,需要移走的石头数量
int func(int x) {                         // func ==> 数组
    int t = 0, last = 0;                  // last上一块石头位置
    for (int i = 1; i <= n + 1; i++) {
        if (num[i] - last < x) t++;
        else last = num[i];
    }
    return t;
}

int bs() {                                // 二分
    while (l != r) {
        int mid = (l + r + 1) / 2;
        int s = func(mid);
        if (s <= m) l = mid;
        else r = mid - 1;
    }
    return r;
}

int main() {
    cin >> r >> n >> m;
    for (int i = 1; i <= n; i++) {        // 输入+处理边界
        cin >> num[i];
        l = min(l, num[i] - num[i - 1]);
    }
    num[n + 1] = r;                       // 起点num[0],终点num[n + 1]
    l = min(l, num[n + 1] - num[n]);
    cout << bs() << endl;
    return 0;
}

⭐二分查找总结

  1. sort
  2. 二分的本质就是删掉不存在答案的区间,前提是单调
    1. 二分查找(二分小数)
    2. 二分的2种特殊情况
    3. 二分答案(对答案进行二分,答案必须满足单调性,用函数替代二分查找中的数组)
  3. 出现最大的最小、最小的最大类似描述,首先想到二分答案
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值