二分法学习

  二分查找看起来确实是一个方便又快捷的方法,但是真正在使用的过程中我们会发现许多令人头疼的事——二分边界。
  首先总结一下关于中间值取整问题:

  • 首先,如果题目属于二分值越大越符合条件,即要求符合条件的最小值,那就是:
while(l<r){
	mid=(l+r)/2;  //或者mid=l+(r-l)/2;
	if(check(mid))r=mid;
	else l=mid+1;
}
return l;

较经典例题:乘法表中第k小数

  • 反之,即 题目属于"二分值越小越符合条件",即要求符合条件的最大值,那就是:
while(l<r){
	mid=(l+r+1)/2;  //或者mid=l+(r-l+1)/2;
	if(check(mid))l=mid;
	else r=mid-1;
}
return l;

较经典例题:每个小孩最多能分到多少糖果
 &emps;有关更新边界的解释,具体可查看某次洛谷日报

例题时间

e,g.1 数列分段

题目描述

对于给定的一个长度为N的正整数数列 \(A_{1\sim N}\),现要将其分成 \(M\)\(M\leq N\))段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列 \(4\ 2\ 4\ 5\ 1\) 要分成 \(3\) 段。

将其如下分段:

\[[4\ 2][4\ 5][1] \]

第一段和为 \(6\),第 \(2\) 段和为 \(9\),第 \(3\) 段和为 \(1\),和最大值为 \(9\)

将其如下分段:

\[[4][2\ 4][5\ 1] \]

第一段和为 \(4\),第 \(2\) 段和为 \(6\),第 \(3\) 段和为 \(6\),和最大值为 \(6\)

并且无论如何分段,最大值不会小于 \(6\)

所以可以得到要将数列 \(4\ 2\ 4\ 5\ 1\) 要分成 \(3\) 段,每段和的最大值最小为 \(6\)

分析

  个人认为这是一道贪心与二分结合的不错的题,有一些绕

  • 总体思路是完善check函数:即在最大和为mid的情况下,计算最多能将数组分为多少组
  • 如果分组数大于所需组数,说明最大值可以再大
  • 相反,说明最大值要变小
int main() {
	int n = read(), m = read();
	vector<int> pre_sum(n + 1, 0);
	int l = 1;
	for (int i = 1; i <= n; i++) {
		int tmp = read();
		l = max(l, tmp);
		pre_sum[i] = pre_sum[i - 1] + tmp;
	}
	long long r = pre_sum[n];
	function<bool(int)>check = [&](int MAX)->bool {
		int cnt = 0;
		int cur = 1;
		for (int i = 1; i <= n; i++) {
			if (pre_sum[i] - pre_sum[cur-1] <= MAX) {
				continue;
			}
			else {
				cnt++;
				cur = i;
			}
		}
		return cnt < m;
	};
	while (l < r) {
		int mid = l + (r - l) / 2;
		if (check(mid))r = mid;
		else l = mid + 1;
	}
	cout << l;
	return 0;
}

e,g.2 Go快读板子使用

  题目很简单,但这是我在洛谷上用Go过的第一道题。我发现Go的读入是真的太慢了!!!故记录下板子的使用。

package main
import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)
var (
    n, m int
    nums []int
)
func main() {
    defer ot.Flush()
	n, m = rnI(), rnI()
    nums = rsI(0, n)
	var l, r int64 = 1, 2e9 + 1
	for l < r {
		var mid int64 = (l + r + 1) / 2
		var sum int64 = 0
		for i := 0; i < n; i++ {
			if mid < int64(nums[i]) {
				sum += int64(nums[i]) - mid
			}
		}
		if sum >= int64(m) {
			l = mid
		} else {
			r = mid - 1
		}
	}
	fmt.Print(l)
}
var (
    ot = bufio.NewWriterSize(os.Stdout, int(1e6))
    in = bufio.NewScanner(os.Stdin)
)

func init()        { in.Split(bufio.ScanWords); in.Buffer(make([]byte, 4096), int(1e9)) }
func rnS() string  { in.Scan(); return in.Text() }
func rnI() int     { i, _ := strconv.Atoi(rnS()); return i }
func rnF() float64 { f, _ := strconv.ParseFloat(rnS(), 64); return f }

func rsI(l, r int) []int {
    t := make([]int, r)
    for i := l; i < r; i++ {
        t[i] = rnI()
    }
    return t
}
posted @ 2022-05-18 16:56  Wasser007  阅读(32)  评论(0编辑  收藏  举报