思路笔记:农夫与奶牛

题面如下:

农夫John建造了一座很长的畜栏,它包括N(2<=N<=100000)个隔间,这些小隔间的位置为x0,x1......xN-1(0<=xi<=1000000000,均为整数,各不相同)。John的c(2<=c<=N)头牛每头分到一个隔间。牛都希望互相离得远点省的互相打扰。怎样才能使任意两头牛之间的最小距离尽可能地大,这个最大的最小距离是多少呢?
 

我们首先想到使用枚举解决上述问题。首先可以通过分析得出一个关键信息:题目要求我们求的最小距离的值是一定小于等于第一个隔间与最后一个隔间的在坐标轴上的距离除上牛的总数-1的值(因为有N头牛就有N-1个间隔)。因此我们可以从该值开始,一个一个地枚举可能的最小值。

大致思路就是:假设一个距离D是我们所要求的答案(其初值就是上述的答案可能的最大值,即平均值),接着我们将奶牛放入隔间中:第一头牛放在坐标为x0的隔间中,也就是第一个隔间。接下来,若第k头牛放在坐标为xi的隔间,则第k+1头牛则放在xj-xi>=D的位于坐标xj的隔间中。若最后能放下c头奶牛,则D=D-1再继续尝试,直到找到一个D,刚好能放下所有的奶牛,我们的答案就为D。

但以上方法在N的值过大时,程序会超时。我们需要优化这个枚举,而二分法恰好可以帮上忙。

以我们上面的枚举思路为基础,融入二分法的思想:我们取D为其最初的最大值( 平均值)与最初的最小值(也就是0)的中位数,带入上述算法中。若D不可行,则更新D可能的最大值修改为当前的D值-1,继续尝试。若D可行,则记住当前D值,更新可能的最小值为当前的D值+1,然后继续尝试。当最小值大于最大值时,循环结束,最后被记住的D值即为我们所求的答案。

代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

/*
农夫John建造了一座很长的畜栏,它包括N(2<=N<=100000)个隔间,这些小隔间的位置为x0,x1......xN-1(0<=xi<=1000000000,均为整数,各不相同)。
John的c(2<=c<=N)头牛每头分到一个隔间。牛都希望互相离得远点省的互相打扰。
怎样才能使任意两头牛之间的最小距离尽可能地大,这个最大的最小距离是多少呢?
*/

int main()
{
	int N, C;//N为隔间数,C为牛的数量
	cin >> N >> C;
	vector<int> coordinate;//存储隔间的坐标
	for (int i = 0, a; i < N; i++)
	{
		cin >> a;
		coordinate.push_back(a);
	}
	sort(coordinate.begin(), coordinate.end());
	int D = 0, Max, Min;
	Max = (coordinate[N - 1] - coordinate[0]) / (C - 1);
	Min = 0;
	int mid = (Max + Min) / 2;
	int Ccount = C - 1;//用于判断牛在最后是否被全部放入,C-1是因为第一个位置肯定会放一个
	vector<int> position;
	position.push_back(coordinate[0]);//记录奶牛被放入的隔间的位置
	while (Max >= Min)
	{
		for (int i = 1; i < N; i++)
		{
			if (coordinate[i] - position.back() >= mid)
			{
				if (Ccount > 0)
				{
					Ccount--;
					position.push_back(coordinate[i]);
				}
			}
		}
		if (Ccount != 0)
		{
			Max = mid - 1;
			mid = (Max + Min) / 2;
		}
		else
		{
			D = mid;
			Min = mid + 1;
			mid = (Max + Min) / 2;
		}
		//以下为每一个循环后的再初始化
		Ccount = C - 1;
		position.clear();
		position.push_back(coordinate[0]);
	}
	cout << D;
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值