4.10 学习笔记之二分答案

啊,我不会二分。刚学。


这里说与贪心结合的二分答案。当然还可以与其他算法结合。

二分答案,可以理解为二分答案所在的区间。

一般能使用二分答案的要求:1.有界性。2.具有单调性。

对于有界性:理解为答案一定在一个区间范围内,是固定的。

对于单调性:显然。这样才能找最优解。

简单来说,二分答案的题目,会出现“最小值最大” or “最大值最小” 的字眼。

思考,这种题目的一个暴力思想:枚举可能的答案,然后找到最优解。

根据二分的思想:我们大可不必枚举每个区间,而是每次找答案可能在的区间,找到其中间值 mid,如果 mid 符合要求,那么会往该区间的右侧去看:是否能找到更优解(体现了单调性);反之,不符合要求,我们往 mid 的左侧去看,去找那个能满足要求的。

在判断是否满足条件时,不需要考虑限制,直接模拟,最后判断是否符合要求即可。

这样,在枚举答案时就避免了很多没用的区间。

综上,二分答案的题目具有一定模板性,分为以下几步:

1.确定当前判断的区间。

2.一个 check 函数模拟判断。

3.满足:向右找最优;不满足:向左找满足。

但时对于二分答案,一定一定需要注意的是,判断边界条件。建议动动脑子,根据题目特性。一般分两种写法,l <= r 是一种,l < r 是另一种。N 老师提倡后者,但我认为前者对我来说更好理解,所以选择前者。

贴个模板:

bool check(int x)
{
    int tot = 0, i = 0, now = 0;
    while(i < n)
    {
        i ++;
        if(a[i] - a[now] < x) tot ++; //统计一次 
        else now = i; //不统计 
    }
    if (超过限制) return 0;
    else return 1;
}
	while(l <= r)
    {
        mid = (l + r) >> 1;
        if(check(mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }

二分答案模板 2:洛谷:P2678 [NOIP2015 提高组] 跳石头

很基础的模板题,本题唯一需要注意的一个特性就是它有一个开头和一个结尾,从 0 开始,到 n + 1 结束。其他就是板子!

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 5e4 + 10;
int d, n, m;
int ans;
int a[N];
bool judge(int x)
{
    int tot = 0, i = 0, now = 0;
    while(i < n + 1)
    {
        i ++;
        if(a[i] - a[now] < x) tot ++;
        else now = i;
    }
    if(tot > m) return 0;
    else return 1;
}
int main()
{
    scanf("%d%d%d", &d, &n, &m);
    for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    a[n + 1] = d; 
    int l = 1, r = d, mid;
    while(l <= r)
    {
        mid = (l + r) >> 1;
        if(judge(mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }
    printf("%d", ans);
}

模板 2:洛谷:P1824 进击的奶牛

和上题几乎一模一样,就是我自己一开始做的时候自己把自己绕晕了。。。

感觉写 check 函数对我来说目前还是有点问题。

没什么,就是注意你枚举的是单个物体还是整块:在这里用 i 枚举牛棚数,用 cnt 记录能放下的牛的头数。然后模拟每次这个牛棚能不能够放下一个牛,如果可以就放下,不可以就开一个新的牛棚。

刚才看的题解没弄懂在枚举啥东西,然后就晕了。感觉还是要多写,有种预感,明天的模拟赛应该会非常非常惨烈。不重要了。

感觉需要读好题,不然真的理解不了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int n, m;
int ans, l = 1, r;
int a[N];
bool check(int x)
{
    int last = 1, i = 1, cnt = 1;
    while(i < n)
    {
        i ++;
        if(a[i] - a[last] >= x) cnt ++, last = i;
    }
    if(cnt >= m) return 1;
    return 0;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++ )
    {
        scanf("%lld", &a[i]);
        r = max(r, a[i]);
    }
    sort(a + 1, a + n + 1);
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(check(mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }
    printf("%d", ans);
}

后记废话:

发现我的基础烂的一批,几乎没有基础。

这两天碎事比较多,压力很大,emmmmm,明天英语课过完就好了罢.......受不了物理化学了,尤其是化学,太抽象。

已经在担忧明天的模拟赛了。啊。没写过二分。很,崩溃。啊。

emmmmmm,我的 oi,只希望越来越好,扎扎实实的补基础罢....

posted @ 2023-04-10 09:56  Moyyer_suiy  阅读(43)  评论(0编辑  收藏  举报