NEUQ-ACM预备队week2-二分与二分答案

题目-查找

在这里插入图片描述

代码实现

#include<iostream>
#include<cstring>
#pragma warning(disable:4996)
int arr[1000006] = { 0 };
using namespace std;
int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) scanf("%d", &arr[i]);
	while (m--)
	{
		int a;
		scanf("%d", &a);
		int mid = 0;
		int l = 1, r = n;
		int ans = -1;
		while (l <= r)
		{
			mid = (l + r) / 2;
			if (arr[mid] > a)r = mid - 1;
			else if (arr[mid] < a)l = mid + 1;
			else
			{
				ans = mid;
				r = mid - 1;
			}
		}
		printf("%d ", ans);
	}
	return 0;
}

难点:

题目本身是一个比较简单的二分思想,给定一个target,寻找这个target的位置。与常规二分法所不同的是,它加入了多个target,这很好实现,简单地循环执行二分查找即可。重点在于,这段数据并不是不重复的,题目又要求我们需要找到这些不重复的数据中最前面的那个数据,这里是额外增加了的一个二分条件应用。

即:当我们找到了一个target,右半边的部分就可以抛弃,从这个位置的左半边继续查找是否有target。(常规中,我们是arr[mid]<target,抛弃右半边)

当然,我们更容易直观想到的是利用while或者for循环,从当前查找到的ans开始,递减地寻找在它之前的重复数据,直到找到一个不重复的数据,输出上一个数据的位置。可惜,这样做会导致超时。

而使用二分查找,只需要做到在找到一个ans后,重复二分查找,这样,如果继续找到了别的ans,这个已经找到的answer会被后找到的、位于它之前的ans刷新,借此来找出最前面的位置下标。

题目-进击的奶牛

在这里插入图片描述

代码实现

#include<iostream>
#include<algorithm>
using namespace std;
bool check(int mid, int* arr,int n,int m)
{
	int num = 1; int last = 0;
	for (int i = 1; i < n; i++)
	{
		if (arr[i] - arr[last] >= mid)
		{
			num++;
			last = i;
		}
	}
	if (num >= m)
		return 1;
	else return 0;
}
int main()
{
	int n, m;
	cin >> n >> m;
	int arr[n]={0};
	for (int i = 0; i < n; i++)cin >> arr[i];
	//序列不为单调序列,所以我们需要排一次序
	sort(arr, arr + n);
	//记录两头牛可能的最远距离,检查是否满足条件
	int R = arr[n - 1] - arr[0],L=0,mid;
	int ans = 1;
	while (L <= R)
	{
		mid = (L + R) / 2;
		//如果满足条件,那么表明这个mid是符合题意的一个mid,
	  //于是更新左区间,观察是否有更大的mid使之成立
		if (check(mid, arr, n, m))
		{
			ans = mid;
			L = mid + 1;
		}
		//不满足条件,表明这个mid已经过大了,
		//于是更新右区间,查找更小的mid
		else
		{
			R = mid - 1;
		}
	}
	printf("%d", ans);
	return 0;
}

题目-跳石头

在这里插入图片描述
在这里插入图片描述

代码实现

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[50010], n, m, L,ans;
bool check(int x)
{
    int last = 0,cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        if (a[i] - last < x)
            cnt++; 
        else
            last = a[i];
    }
    if (cnt > m)
        return false;
    return true;
}
int main()
{
    scanf("%d%d%d", &L, &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    a[++n] = L;
    int l = 0, r = L;
    while (l <= r)
    {
        int mid = (l + r) / 2;
        if (check(mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else
            r = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
}

难点

对于明显要二分答案来做的题,需要书写的二分check条件,在本题中,当距离低于mid时,由于要保证是最大mid,所以小于mid的距离都应该被抛弃,在本题中也就是num++(即移走这块石头),同时,需要注意:虽然题给条件是不允许移走最后一块石头,但是,如果最后一块与倒数第二块之间的距离小于mid,我们也是不可以这样跳过去的,所以实际操作中,最后一块石头也仍然是我们检验的条件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值