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,只希望越来越好,扎扎实实的补基础罢....