1.烦人的高考志愿
下面的这个题目应该是普通的二分里面的非常经典的这个题目了,我觉得还是有一些这个难度的,这个题目需要我们找到的事差值的绝对值最小的这个符合要求的元素,这个题目也可以使用二分的思想进行求解
在这个输入数据里面4表示的就是这个学校的预估的分数,3表示的就是三个成绩,我们需要做的就是根据三个数数据去四个数据最近的数据,不管是大还是小,例如下面的这个和500最接近的数据,就是513,和600最接近的数据,就是598,我们一次去计算这个差值,最后对于这个差值进行求和输出即可
下面的这个是代码:
1)首先数据的输入并且进行排序,只有排序之后我们才可以使用我们的这个二分的算法;
2)调用这个find函数找到第一个大于等于b的这个元素,然后我们去比较这个pos位置的元素和pos-1位置的元素和我们的这个给定数据的绝对值的大小比较,看看那一个是更加符合我们的这个题目的要求的,因为只有这两个可能是最终的答案,这个是我们进行二分筛选之后的,只有这两个事一个大于我们的这个给定元素,一个小于我们的给定元素,我们调用这个绝对值函数把这个全部转换成为正数,求得里面的最小值即可;
3)在这个find函数里面,我们就是使用这个二分的算法进行比较,把这个数据区间划分成为这个大于等于x的部分和小于x的这个部分即可,返回的就是这个数组里面的第一个大于等于x的下标;
4)大家可以发现这个里面出现了a[0]这个元素,这个主要是为了反之报错的出现,因为我们进行这个数据输入的时候是从1开始的,但是我们在min函数调用的时候出现了这个pos-1的情况,因此这个时候如果pos=1,pos-1就是0,因此我们需要手动的对于这个0下标进行赋值,我们赋值一个非常大的数据,这样求最小值的时候就没有影响了,这个叫做加上左右护法,防止因为数组只有一个元素的时候报错的情况;
#include<iostream>
#include<algorithm>
typedef long long LL;
using namespace std;
const int N = 1e5 + 10;
int n, m;
int a[N];
int find(LL x)
{
int left = 1, right = m;
while (left < right)
{
int mid = (left + right) / 2;
if (a[mid] >= x) right = mid;
else left = mid + 1;
}
return left;
}
int main()
{
cin >> m >> n;
for (int i = 1; i <= m; i++)
{
cin >> a[i];
}
sort(a + 1, a + 1 + m);
a[0] = -1e7 + 10;
LL ret = 0;
for (int i = 1; i <= n; i++)
{
LL b; cin >> b;
int pos = find(b);
ret += min(abs(a[pos] - b), abs(a[pos - 1] - b));
}
cout<<ret<<endl;
return 0;
}
2.排序数组里面的元素出现位置
下面的这个算是我们的二分的模版题目,老师在这个题目介绍的也算是非常的详细,但是当我们真正的学完这个二分的章节之后,就会意识到,其实这个二分的世界面没有真正的这个模版,因为基本上根据不同的题目,我们都需要考虑特殊情况和临界情况,也需要我们自己去学会分析问题,找出来这个二分的破局点,这个也是我们的二分里面比较容易出错的地方,这个算是我学完这个相关内容之后一个最大的感触吧;
下面的这个输入的内容就是我们的一个简单的数组,需要在这个数组里面去找到目标元素,且这个元素出现的次数应该不是一次,需要找到第一次出现的位置和最后一次出现的文字,返回这两个位置对应的下标,如果没有找到,返回-1即可;
下面的这个是代码:
1)首先对于这个空数组的情况进行判断,这个时候我们是直接返回-1的;
2)下面整体上去看,明显可以看到的是2个while循环,这个意图其实是非常明显的,就是两个循环分别负责找到我们的这个数组里面元素第一次出现的这个位置和最后一次出现的这个位置;
3)第一个循环主要找的就是这个元素第一次出现的这个位置,我们需要把这个给定的数组划分为大于等于ret和小于ret两个部分,并对于这两个部分进行二分的处理,不断缩小我们的这个查找的空间;
4)第二个循环主要还是针对于这个最后一次出现的这个元素进行处理,因此这个时候划分的标准,就是小于等于ret和大于ret的两个部分的内容,并且进行二分的处理;
5)其中第一次循环结束之后进行了这个判断,如果数组里面本来就没有这个元素,其实这个时候我们的left和right指向的是一个位置,因此这个时候无论我们使用哪一个下标进行判断都是没有问题的,因为本质上都是一样的;
定义这个releft这个变量主要还是记录一下这个位置,然后找到这个right的时候,直接返回这两个定义的变量即可,就是在我们的数组里面的出现第一个目标元素的位置,以及出现最后一个目标元素的位置;
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int n=nums.size();
int left=0,right=n-1;
if(n==0) return {-1,-1};
while(left<right)
{
int mid=(left+right)/2;
if(nums[mid]>=target) right=mid;
else left=mid+1;
}
if(nums[left]!=target) return {-1,-1};
int releft=left;
left=0,right=n-1;
while(left<right)
{
int mid=(left+right+1)/2;
if(nums[mid]<=target) left=mid;
else right=mid-1;
}
return {releft,left};
}
};