day5 - 更上一层楼
二分
二分查找
二分查找
- 闭区间 [l, r]
注意求mid的三种解法
(1) mid = (l + r)/2
(2) mid = (l + r)>>1
(3) mid = l + (r - l)/2
思考三种写法的不同用处
- 左闭右开 [l, r)
1 |
|
二分查找边界
有一种情况,当数据出现多个相同的数字是,二分查找应该返回哪一个位置?
例如:
n = 5
a[] = {1, 1, 1, 1, 1}
x = 1
返回 ans = 3
显然这种二分查找只会优先返回相同数字中间的位置或偏左一位,但是我们通常想要的是第一个或这是最后一个相同数字的位置
- 二分查找左边界
道理其实很简单:我们在找到x的时候a[mid] == x,还要向左边看,向左折半查找,等同与 if(x == a[mid]) right = mid - 1,然后和 if(x < a[mid]) right = mid - 1合并一下,就是 if(x <= a[mid]) right = mid - 1了
1 |
|
- 二分查找右边界
同理可得
例题1:洛谷题号1678
烦恼的高考志愿
题目背景
计算机竞赛小组的神牛 V 神终于结束了高考,然而作为班长的他还不能闲下来,班主任老 t 给了他一个艰巨的任务:帮同学找出最合理的大学填报方案。可是 v 神太忙了,身后还有一群小姑娘等着和他约会,于是他想到了同为计算机竞赛小组的你,请你帮他完成这个艰巨的任务。
题目描述
现有 $m$ 所学校,每所学校预计分数线是 $a_i$。有 $n$ 位学生,估分分别为 $b_i$。
根据 $n$ 位学生的估分情况,分别给每位学生推荐一所学校,要求学校的预计分数线和学生的估分相差最小(可高可低,毕竟是估分嘛),这个最小值为不满意度。求所有学生不满意度和的最小值。
输入格式
第一行读入两个整数 $m,n$。$m$ 表示学校数,$n$ 表示学生数。
第二行共有 $m$ 个数,表示 $m$ 个学校的预计录取分数。第三行有 $n$ 个数,表示 $n$ 个学生的估分成绩。
输出格式
输出一行,为最小的不满度之和。
样例 #1
样例输入 #1
1 | 4 3 |
样例输出 #1
1 | 32 |
提示
数据范围:
对于 $30%$ 的数据,$1\leq n,m\leq1000$,估分和录取线 $\leq10000$;
对于 $100%$ 的数据,$1\leq n,m\leq100000$,估分和录取线 $\leq 1000000$ 且均为非负整数。
这题是二分查找的标准例题。
一般枚举算法的思路:两层循环,对每一位同学从头到尾搜索学校,找到分值相差最小的学校,使得总不满意度最小。时间复杂度O(n*m)
二分答案的思路:
这不是一般二分查找,不一定能找到估分和学校分数相同的情况。先将学校排序,成为单调的问题,从小到大,再单纯的二分法,分数大于mid,向右寻找,分数小于mid,向左寻找。如此最后left停在了刚好左边小,右边大的位置,也就是分数相近,比较左右不满意值,累加起来。时间复杂度O(n*logm)
关于快排:
1 | sort(a+1,a+m+1);//对学校分数快速排序 |
这个是C++中algorithm库中的快排函数sort(),能从小到大排序。
关键代码:(选择什么样二分的形式)
1 | int l=1,r=m; |
有没有特殊情况:不用二分的(这样可以优化算法,相当于剪枝)
最后,这题有没有别的做法:比如排序?
二分答案
二分答案与二分查找类似,也就是对这有单调性的答案进行二分,用于求解满足某种条件下的最大(小)值
二分答案的常见模版:
1 | int l=1,r=max; |
例题2:洛谷题号1676 1824(双倍经验)
[USACO05FEB] Aggressive cows G
题目描述
农夫约翰建造了一座有 $n$ 间牛舍的小屋,牛舍排在一条直线上,第 $i$ 间牛舍在 $x_i$ 的位置,但是约翰的 $m$ 头牛对小屋很不满意,因此经常互相攻击。约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其它牛尽可能远的牛舍。也就是要最大化最近的两头牛之间的距离。
牛们并不喜欢这种布局,而且几头牛放在一个隔间里,它们就要发生争斗。为了不让牛互相伤害。约翰决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是多少呢?
输入格式
第一行用空格分隔的两个整数 $n$ 和 $m$;
第二行为 $n$ 个用空格隔开的整数,表示位置 $x_i$。
输出格式
一行一个整数,表示最大的最小距离值。
样例 #1
样例输入 #1
1 | 5 3 |
样例输出 #1
1 | 3 |
提示
把牛放在 $1$,$4$,$8$ 这三个位置,距离是 $3$。容易证明最小距离已经最大。
对于 $100%$ 的数据,$2 \le n \le 10^5$,$0 \le x_i \le 10^9$,$2 \le m \le n$。
不保证 $a$ 数组单调递增。
做题思路:
二分答案的核心是:存在一个分界线(即答案ans),大于 ans 时没有合法答案;小于等于 ans 时,一定存在答案。求 ans 的过程。
核心:通过阅读题目,大致可以感受到一种贪心算法:从最左端开始,每隔一个单位距离 x 就安置一头牛,一定是能安置就安置,可以证明安置了就一定比不安置更优,最后统计安置了几头牛即可。
注意折半查找的边界,是k>=m还是k>m,看题目希望什么,题目希望ans尽可能的大,所以应该是 二分查找右边界,等于的时候优先去右边
代码:
1 |
|
习题1:洛谷P2440 木材加工(必做)
标准的二分答案,几乎和上题一模一样,比上题更简单。
1 |
|
这里用maxlen不用INF是我们可以判断出ans的大小不会超过maxlen,其实上题也是可以用这种方法的,但是在对数函数log的作用下,影响不大。
习题2:洛谷P2678 跳石头(选做)
注意:最短跳跃距离尽可能长,应该是二分查找右边界,等于的时候优先去右边
1 |
|