洛谷 P6174 [USACO16JAN]Angry Cows S (尚贤)

题目传送门

核心思想:二分答案

1.什么是二分答案

在一个单调不递减或单调不递增的区间里,我们要寻找一个答案使得题目的条件成立,最简单的方法是枚举,时间复杂度是O(n),可以应付一些数据较小的题目;也可以通过二分的方法来找到这个值,时间复杂度为O(logn),是时间上比较优秀的算法。

2.为什么本题可以二分答案

因为我们要寻找的R值是一个自然数,当然就是在一个递增的区间里寻找,符合二分答案的特点。

3.具体地?

我们把原数组升序排序,取到最大值,只要仔细思考就能明白,我们要寻找的R一定在00~\max(a[i])max(a[i])之间。

那么,排序之后令l=0,r=a[n],每次检验mid是否符合条件即可。

如果符合条件,那么这个值在小一些说不定也可以,就把r缩过来,令r=mid-1

反之,说明这个数过大了,就把l缩过来,令l=mid+1

怎么检验呢?

二分答案的精髓正在于判断函数

我们用一个变量模拟当前爆炸的进度,只要爆炸进度够不到下一个能触及的范围,那么我们就需要再发射一头,只要发射的奶牛数超过了题目中的k,那么就不符合条件。

4.代码实现

(详见注释)

#include<cstdio>
#include<iostream>
#include<algorithm>//sort必备
#include<climits>//这个是支持INT_MIN的头文件
using namespace std;
int n,k;
int x[(int)5e4+10];
//皆和题目中意义相同
bool check(int R)
{
    int s=0,now=INT_MIN;
    /*
    s表示已经发射了多少奶牛
    now表示当前已爆炸的范围
    为什么取INT_MIN呢,因为这样可以保证至少发射一头
    */
    for(register int i=1;i<=n;i++)
    {
        if(now<x[i]-R)
        {
            //如果当前已爆破的范围小于下一个能触及的范围
            s++;
            //发射数加1
            now=x[i]+R;
            //更新当前范围
            if(s>k)return false;
            //如果超过了限定数,那么就一定pass了
        }
    }
    return true;
    //到最后都没pass,说明成功爆破
}
int main()
{
    freopen("cpp.in", "r", stdin);
    freopen("cpp.out", "w", stdout);
    scanf("%d%d",&n,&k);
    for(register int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
    }
    sort(x+1,x+n+1);
    int l=0,r=x[n];
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
        {
            r=mid-1;
        }
        else
        {
            l=mid+1;
        }
    }
    printf("%d\n",l);
    //二分模板
    return 0;
}

me的AC代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#define SIZE (int)1e4 * 5 + 10
#define INF 0x3f3f3f3f * 2
using namespace std;
int a[SIZE];

int my_search(const int &, const int &);
bool pan_duan(const int &, const int &, const int &);

int main() {
	freopen("cpp.in", "r", stdin);
	freopen("cpp.out", "w", stdout);
	int n, k;
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
	}
	sort(a + 1, a + n + 1);
	printf("%d\n", my_search(n, k));
	return 0;
}

int my_search(const int &n, const int &k) {
	int left = 0, right = a[n];
	while (left <= right) {
		int mid = (left + right) >> 1;
		if (pan_duan(n, k, mid)) {
			right = mid - 1;
		} else {
			left = mid + 1;
		}
	}
	return left;
}
bool pan_duan(const int &n, const int &k, const int &R) {
	int num = 0, now = -INF;
	for (int i = 1; i <= n; ++i) {
		if (a[i] - R > now) {
			now = a[i] + R;
			++num;
			if (num > k) {
				return false;
			}
		}
	}
	return true;
}
发布了33 篇原创文章 · 获赞 0 · 访问量 167

猜你喜欢

转载自blog.csdn.net/weixin_42790071/article/details/105539945