二分查找求最大值最小化(最小值最大化)问题

P2678 跳石头

题目描述

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 NN 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 MM 块岩石(不能移走起点和终点的岩石)。

输入格式:

第一行包含三个整数 L,N,ML,N,M ,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L \geq 1L≥1 且 N \geq M \geq 0N≥M≥0 。

接下来 NN 行,每行一个整数,第 ii 行的整数 D_i( 0 < D_i < L)Di​(0<Di​<L) , 表示第 ii 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式:

一个整数,即最短跳跃距离的最大值。

输入样例#1:

25 5 2 
2
11
14
17 
21

输出样例#1: 

4

此题要求最短距离最大,对于最短距离我们知道其范围是1~L,那么可以在该单调区间内进行二分查找不断缩小范围。

那么还需要一个判断函数judge来判断当前距离作为最短距离是否是可行解。如果是可行解,但有可能它不是最优解,那么因为求最大值我们还需要继续向其右部区间查找是否有更优解;如果不是可行解,那么可行解只可能在其左部区间,二分向左部查找。

#include<iostream>
using namespace std;
int l, n, m;
int a[50001];
bool judge(int x)                     //判断该x距离是否是一个合法解
{
	int before = 0;
	int num = 0;
	for (int i = 1; i <= n+1; i++)
	{
		if (a[i] - before < x)        //如果该距离比最短距离还短,说明该石头需移走,计数器加1
			num++;
		else                          
			before = a[i];
	}
	if (num > m)                      //需移走的石头总数超过了m,说明这个距离是非法解
		return false;
	return true;
}
int main()
{
	int ans;
	cin >> l >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	a[n + 1] = l;                     //注意审题
	int left = 1, right = l, mid;
	while (left <= right)             //基本的二分查找
	{
		mid = (left + right) / 2;
		if (judge(mid))               //合法继续去右边找更大的,非法则去左边找合法的
		{
			ans = mid;
			left = mid + 1;
		}
		else
			right = mid - 1;
	}
	cout << ans << endl;
	return 0;
}

这类题目的核心是找到二分的范围区间进行二分查找,二分查找的条件通过一个judge函数检测,judge函数在不同题中有不同写法,但目的都是来判断一个解是否合法。

P1182 数列分段

题目描述

对于给定的一个长度为N的正整数数列 A-iA−i ,现要将其分成 M(M≤N)M(M≤N) 段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列 42451 要分成 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 。

输入输出格式

输入格式:

第 1 行包含两个正整数N,M。

第 2 行包含 NN 个空格隔开的非负整数 A_iAi​ ,含义如题目所述。

输出格式:

一个正整数,即每段和最大值最小为多少。

输入样例#1: 

5 3
4 2 4 5 1

输出样例#1: 

6

对于 20% 的数据,有 N≤10;

对于 40% 的数据,有 N≤1000 ;

对于 100% 的数据,有 N≤100000,M≤N,Ai​ 之和不超过 10^9 。

#include<iostream>
#include<algorithm>
using namespace std;
int n, m;                              //分m段,则要划出m-1条线
int a[100001];
bool judge(int x)                      //x是所有段中的最大值,希望其最小
{
	int line = 0;
	int sum = 0;
	for (int i = 1; i <= n; i++)
	{
		if (sum + a[i] > x)            //如果和超过了最大值说明需要将其分段,线段数加1,重新开始计算sum
		{
			line++;
			sum = a[i];
		}
		else
			sum += a[i];
	}
	if (line >= m)                     //所加的线段数超过了要求的m-1条,说明这个最大和是非法解
		return false;
	return true;
}
int main()
{
	int sum = 0, ans = 0, max=-1;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		if (a[i] > max)
			max = a[i];
		sum += a[i];
	}
	int l = max, r = sum, mid;          //注意二分的范围区间!!最小的和是最大的一个数,最大的和是所有数的总和!!
	while (l <= r)                      //开始二分选择,合法则向左找还有没有更优解,不合法则向右找合法解
	{
		mid = (l + r) / 2;
		if (judge(mid))
		{
			ans = mid;
			r = mid - 1;
		}
		else
			l = mid + 1;
	}
	cout << ans << endl;
	return 0;
}

P1577 切绳子

题目描述

有N条绳子,它们的长度分别为Li。如果从它们中切割出K条长度相同的

绳子,这K条绳子每条最长能有多长?答案保留到小数点后2位。

输入格式:

第一行两个整数N和K,接下来N行,描述了每条绳子的长度Li。

输出格式:

切割后每条绳子的最大长度。

输入样例#1: 

4 11
8.02
7.43
4.57
5.39

输出样例#1: 

2.00

使用二分法范围寻找绳子长度的最优解,分出的K段绳子长度范围在0~max之间,max是所有绳子长度的最大值。

#include<iostream>
#include<cstdio>
using namespace std;
int n, k, num = 0;
double a[10001], sum = 0;
bool judge(double x)                  //x是绳子的长度,期望其最大
{
	num = 0;
	for (int i = 1; i <= n; i++)
		num += floor(a[i] / x);
	if (x*k > sum||num<k)    //如果K条绳子总长超过绳子总长或该长度的绳子无法分出K条,那么说明x过大,需要向左找合法解
		return false;
	return true;
}
int main()
{
	cin >> n >> k;
	double max = -1, ans = 0;
	for (int i = 1; i <= n; i++)      
	{
		cin >> a[i];
		sum += a[i];
		if (a[i] > max)
			max = a[i];
	}
	double l = 0, r = max+1, mid;
	while (l<=r)
	{
		mid = (l + r) / 2;
		if (judge(mid))
		{
			ans = mid;
			l = mid+0.01;             //如果合法就向右找是否有更长的绳长作为更优解
		}
		else
			r = mid-0.01;
	}
	printf("%.2f\n", ans);
	return 0;
}

此外这个题还需要注意的是精度问题!

猜你喜欢

转载自blog.csdn.net/slience_646898/article/details/81086695