• 二分搜索算法笔记C++


    1.背景

    1.1 结论

    在有序数组中查找某个值,或者在求最优解问题时,二分搜索非常有用。思想一般是先假定一个解,并判断是否可行,接着缩小解的范围继续判断。

    1.2 概念

    二分搜索法,是通过不断缩小解可能存在的范围,从而求得问题最优解的方法。在程序设计竞赛中,经常可以见到二分搜索法和其他算法结合的题目。

    2.二分搜索

    2.1思想

    二分答案转化为判定。一个宏观的最优化问题也可以抽象为函数,其“定义域”是该问题下的可行方案,最这些可行方案进行评估得到的数值构成函数的“值域”,最优解就是评估最优的方案,(不妨设评分越高越优)。假设最优解评分为S,显然对于所有大于S的值,都不存在一个合法的方案达到该评分,否则就与S的最优性矛盾;而对于所有< S 的值,一定存在一个合法的方案达到或者超过该评分,因为最优解就满足这个条件。这样问题的值域就具有一种特殊的单调性——在S的一侧合法、另一侧不合法,可以通过二分找到这个分界点S。借助二分,我们把求最优解问题,转化为给定一个值mid,判定是否存在一个可行的方案评分达到mid的问题。

    2.2例题

    2.2.1题意简述:有N本书排成一行,已知第 i 本书的厚度是Ai。把它们分成连续的M组,使T最小化,其中T表示厚度之和最大的一组的厚度。

    2.2.2例题解析:题目出现了类似“最大值最小”的含义,可以看出答案具有单调性(也就是说这个答案满足在一定单调区间内)。所以可以使用二分搜索,而判定函数如何写呢?假设最大组为size,一共有m组,那么m*size一定小于所有厚度之和;那我们顺序用size减去每本书的厚度,看看能分为几组和小于size且最大,这样一来如果组数小于m,那我们可以通过将一组分割成好几组来凑到m,此时最大厚度不变。而如果组数大于m,我们则可以通过合并来使组数等于m,但这样一来最大厚度就必然大于size。而答案必然存在于1到inf(无穷大),故我们只需找到临界点,即为答案。

    2.2.3 代码示例:

    bool check(int size){
    	int cnt = 1;
    	int tmp = size;
    	for(int i = 0;i < n;i++){
    		if(tmp - a[i] >= 0)	tmp -= a[i];
    		else cnt++,tmp = size - a[i];
    	}
    	return cnt <= m;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i = 0;i < n;i++)	scanf("%d",a+i);
    	int l = 0,r = inf;
    	while(l < r){
    		int mid = (r + l )/2;
    		if(check(mid)) l = mid + 1;
    		else r = mid;
    	}
    	printf("%d
    ",l); 
    	return 0;
    } 

    2.3 最大化平均值

    上面的例题可以用“最小化最大值”来形容,当然也有“最大化最小值”,不过大同小异,二分搜索还有一种应用,用来解决最大化平均值问题。

    有n个物品的重量和价值分别是 wi 和 vi 。从中选出 k 个物品使得单位重量的价值最大。

    2.3.1 例题解析:上述问题贪心策略是解决不了的,因为有俩个权值啊。对于这个问题是可以用二分搜索来解决。首先定义:

                         条件C(x) = 可以选择使单位重量的价值不小于x。

    因此原问题就成了求满足C(x)的最大x。而:

    • 价值和/重量和>=x
    • 价值和-重量和*x>=0
    • 和(价值-重量*x)>=0
    • 可以对(价值-重量*x)的值进行贪心的选取,选取最大的k个 和>=0

    2.3.2 代码示例:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    const int maxn = 10010;
    const int inf = 1e9+7;
    double w[maxn],v[maxn],y[maxn];
    int n,k;
    
    bool check(double size){
    	for(int i = 0;i < n;i++)
    		y[i] = v[i] - w[i]*size;
    	sort(y,y+n);
    	double sum = 0;
    	for(int i = n-1;i >= n-k;i--){
    		sum += y[i];
    	}
    	return sum >= 0;
    }
    int main(){
    	scanf("%d%d",&n,&k);
    	for(int i = 0;i < n;i++)	scanf("%lf%lf",w+i,v+i);
    	double l = 0,r = inf;
    	for(int i = 0;i < 100;i++){
    		double mid = (l + r)/2;
    		if(check(mid))	l = mid;
    		else r = mid;
    	}
    	printf("%.2f",r);
    	return 0;
    }
  • 相关阅读:
    C# 使用Dev控件为gridView添加复选框,实现多选和单选
    C# 关于LINQ基础随笔
    Camstar客制化开发做查询操作(Designer中存放SQL语句)
    ASP.NET 实现自动登录(自定义自动登录时间期限)
    ASP.NET 页面生命周期-动态控件编程(动态添加控件并使用动态控件值)
    Camstar客制化开发查询数据库数据并显示在JQDataGrid表中(SQL语句编写在UserQuery中)同时获取JQDataGrid中值回传到Designer中
    Camstar客制化开发查询数据库数据并显示在JQDataGrid表中(SQL语句编写在代码中)
    WPF开发中常用的几种布局元素
    Cocos2dx 内存管理机制(1)
    cocos2dx 坐标系统详解
  • 原文地址:https://www.cnblogs.com/long98/p/10352155.html
Copyright © 2020-2023  润新知