【算法】白给题(贪心,二分)

题目描述
给定N个自然数,你需要选择M个,使得M个数中两两之间的差的绝对值的最小值尽可能大,求这个最大值。

输入格式
第一行两个空格隔开的正整数N,M,表示自然数个数和要选的数的个数。
接下来一行为N个空格隔开的给定自然数。

输出格式
一个整数。

样例输入
3 2
1 2 3
样例输出
2

★解题思路

关键搞清楚什么是“两两之间的差的绝对值的最小值尽可能大”:假设给定一个序列,最小值为10,最大值为90,要求选择两个数,那么显然是选最大值和最小值。
在这里插入图片描述
如果要求选择3个数,那么应该选择最大数和最小数,以及中间那个数,这里需要考虑一个理论最大间隔的概念:在这里插入图片描述
所以此时理论上应该选择的数就是50
在这里插入图片描述
继续,如果要选4个数,也就是要给一个序列分3段,那么有:
在这里插入图片描述
理论上的一种选择是这样的:
在这里插入图片描述
具体思路:先计算出理论最大间隔,并进行判断是否符合要求,符合则为答案,不符合则减小最大间隔的长度,继续判断。

#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
bool jud(int ans,int a[])
{
	int index = 0, num = 1;
	for(int i=1; i<n; i++) {
		if(a[i] >= ans + a[index]) {
			index = i;
			num++;
		}
		if(num >= m) return true;
	}
	return false;
}
int main()
{
	scanf("%d %d",&n,&m);
	int a[n];
	for(int i=0; i<n; i++) {
		scanf("%d",&a[i]);
	}
	sort(a,a+n);//题目没说有序,输入数据当无需处理,故先排序。
	int ans=(a[n-1]-a[0])/(m-1);
	while(!jud(ans--,a));//依次减小最大间隔值,直到满足要求 
	printf("%d\n",ans+1);
	return 0;
}

★二分改进

最大间隔一个个减少并去判断太慢了,会超时,故应二分。

#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
bool jud(int ans,int a[])
{
	int index = 0, num = 1;  
	for(int i=1; i<n; i++) {
		if(a[i] >= ans + a[index]) {
			index = i;
			num++;
		}
		if(num >= m) return true;
	}
	return false;
}
int bi(int left,int right,int a[])
{
	if(left > right) return 0;
	int mid = left + (right - left)/2; //区间中点,避免溢出式写法 
	if(jud(mid,a)) { //判断当前选取的间隔mid是否满足要求 
		return max(bi(mid+1,right,a),mid); //向mid偏大的方向二分 
	} else {
		return bi(left,mid-1,a);//向mid偏小的方向二分 
	}
}
int main()
{
	scanf("%d %d",&n,&m);
	int a[n];
	for(int i=0; i<n; i++) {
		scanf("%d",&a[i]);
	}
	sort(a,a+n);
	printf("%d\n",bi(0,(a[n-1]-a[0])/(m-1),a));
	return 0;
}

时间复杂度为O(nlogn)

二分法举例说明:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
继续采用二分,选择13不符合要求;选择6不符合要求;选择3符合要求;选择4符合要求;选择5符合要求,并退出二分。
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值