二分查找习题

度度熊很喜欢数组!! 

我们称一个整数数组为稳定的,若且唯若其同时符合以下两个条件: 

1. 数组里面的元素都是非负整数。 
2. 数组里面最大的元素跟最小的元素的差值不超过 11。

举例而言,[1,2,1,2][1,2,1,2] 是稳定的,而 [−1,0,−1][−1,0,−1] 跟 [1,2,3][1,2,3] 都不是。 

现在,定义一个在整数数组进行的操作: 

* 选择数组中两个不同的元素 aa 以及 bb,将 aa 减去 22,以及将 bb 加上 11。 

举例而言,[1,2,3][1,2,3] 经过一次操作后,有可能变为 [−1,2,4][−1,2,4] 或 [2,2,1][2,2,1]。 

现在给定一个整数数组,在任意进行操作后,请问在所有可能达到的稳定数组中,拥有最大的『数组中的最小值』的那些数组,此值是多少呢? 

Input

输入的第一行有一个正整数 TT,代表接下来有几组测试数据。 

对于每组测试数据: 
第一行有一个正整数 NN。 
接下来的一行有 NN 个非负整数 xixi,代表给定的数组。

* 1≤N≤3×1051≤N≤3×105 
* 0≤xi≤1080≤xi≤108 
* 1≤T≤181≤T≤18 
* 至多 11 组测试数据中的 N>30000N>30000

Output

对于每一组测试数据,请依序各自在一行内输出一个整数,代表可能到达的平衡状态中最大的『数组中的最小值』,如果无法达成平衡状态,则输出 −1−1。

Sample Input

2
3
1 2 4
2
0 100000000

Sample Output

2
33333333

这道题一开始题意都理解不好,尤其是那个“代表可能到达的平衡状态中最大的『数组中的最小值』”,这么绕口。。。
好了,不吐槽了

其实就是让你找平衡状态时,数组中的最小值,达到平衡时数组中也就俩数,因为要保证最大值最小值之差小于1

解题的思路是这样的,总体上是用的二分法,先找出数组中的中间值,接着遍历数组中的每个元素,如果小于数组,x加上这个差,如果大于数组,y加上这个差的一半;最后比较这两个数的大小,就是二分的步骤。

大概就是这样,要加把劲了

补充一个小知识点:
关于%I64d和%lld的问题
两种都是C语言中格式化输出64位整型的正确做法。
区别在于编译器不同。
gcc(mingw32),g++(mingw32)只支持%I64d的输出方式。 
gcc(linux i386),g++(linux i386)只支持%lld的输出方式。
另外,mac上的gcc/g++不支持%I64d的方式。

最后把代码码一遍吧
 


#include<iostream>
#include<stdio.h>
#define maxn 300010
#define LL long long
LL a[maxn];
using namespace std;

int main(){
    int N;
    cin>>N;
    while(N--){
    	LL T;
		cin>>T;
		for(int i=0;i<T;i++){
			cin>>a[i];
		} 
		LL mid;
		LL l=0;
		LL r=0;
		while(l+1<r){
			l=a[0];
			r=a[0];
			for(int i=0;i<T;i++){
				if(l>a[i]){
					l=a[i];
				}
				if(r<a[i]){
					r=a[i];
				}
			}
			mid=(l+r)/2;
			LL x=0;
			LL y=0;
			for(int i=0;i<T;i++){
				if(a[i]<mid){
					x+=mid-a[i];
				}else{
					y+=(a[i]-mid)/(LL)2;
				}
			}
			if(x>=y){
				l=mid;
			}else{
				r=mid;
			}
		}
		printf("%l64d",l);
    }
    
}

 

<think>嗯,用户想要关于C++中二分查找法的简单练习题示例。我需要先回顾一下二分查找的基础知识,然后结合提供的引用内容来生成合适的练习题。首先,二分查找是一种在有序数组中高效查找元素的算法,时间复杂度是$O(\log n)$。用户可能刚开始学习算法,所以练习题应该从简单入手,逐步加深难度。 根据引用[2]和引用[4],LeetCode上有一些二分查找的题目,比如寻找目标值的第一个和最后一个位置。不过用户需要的是简单题,所以应该先找基础的实现题。比如,引用[4]中的示例代码就是标准的二分查找实现,可以用来作为第一个练习题。 然后,引用[3]中提到求a的m次方来判断是否满足条件,这可能涉及二分查找的变种,比如在实数范围内进行二分,但可能对初学者来说有点难。不过可以考虑将其转化为一个简单的问题,比如求平方根,这样更基础。 另外,引用[1]虽然主要是关于哈希的题目,但其中提到的在有序数组中找重复元素或交集,可能也可以改编成二分查找的问题,比如在有序数组中查找是否存在某个元素,或者统计出现次数。 现在,我需要设计几个简单的练习题,适合刚学完二分查找的人。题目应该覆盖基本的实现、查找目标值、查找插入位置、求平方根等基础应用。同时,参考已有的引用内容,可以结合LeetCode的题目,但需要简化题目描述,使其适合练习。 第一个题目可以是标准的二分查找实现,比如在有序数组中查找目标值的位置,如果不存在返回-1。第二个题目可以是查找插入位置,即找到目标值应该插入的位置,这也是LeetCode的35题。第三个题目可以是求非负整数的平方根,取整数部分,这对应LeetCode 69题。第四个题目可以是统计有序数组中某个元素出现的次数,这需要找到第一个和最后一个位置,然后计算差值,如LeetCode 34题,但可以拆分成两个步骤,先找是否存在,再找次数。 另外,可以加入一个旋转数组的题目,比如LeetCode 33题,但可能难度稍高,不过如果用户需要简单题,可能需要调整。或者考虑更基础的变种,比如寻找旋转点,但可能属于中等难度。这里可能需要判断,如果用户需要简单题,可能暂时不包括这个,或者作为进阶题。 综上,我应该生成四个简单的练习题,涵盖基本实现、插入位置、平方根和统计次数。同时,在答案中提供思路和代码示例,参考引用中的代码结构,使用C++实现,并保持代码简洁。还要确保每个题目都有示例输入输出,帮助用户理解。 需要检查是否有引用内容可以支持这些题目。比如,引用[4]中的代码示例可以作为第一个题目的参考,引用[2]中提到的寻找第一个和最后一个位置可以用于统计次数的题目。求平方根可能参考引用[3]中的实数二分方法,但需要简化。 最后,确保问题描述清晰,示例正确,并且代码符合C++语法。同时,按照用户要求,在回答末尾添加相关问题,比如优化方法、时间复杂度分析等,帮助用户进一步学习。</think>### C++ 二分查找法简单练习题示例 以下为适合初学者的二分查找习题,包含基础实现和应用场景,附解题思路和代码示例: --- #### **1. 基础二分查找实现** **题目**:在**升序数组**中查找目标值,返回索引,若不存在返回-1。 **示例输入**:`nums = [1,3,5,7,9], target = 5` **示例输出**:`2` **代码框架**: ```cpp int search(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; // 防止溢出 if (nums[mid] == target) return mid; else if (nums[mid] < target) left = mid + 1; else right = mid - 1; } return -1; } ``` **关键点**:循环条件为`left <= right`,确保搜索区间闭合[^4]。 --- #### **2. 查找目标值的插入位置** **题目**:如果目标值不在数组中,返回它应该插入的位置索引(保持数组有序)。 **示例输入**:`nums = [1,3,5,6], target = 2` **示例输出**:`1` **解题思路**: - 当循环结束时,`left`指针的位置即为插入位置[^2]。 **代码片段**: ```cpp int searchInsert(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] >= target) right = mid - 1; else left = mid + 1; } return left; } ``` --- #### **3. 求非负整数的平方根(取整数部分)** **题目**:计算非负整数`n`的平方根,结果向下取整。 **示例输入**:`n = 8` **示例输出**:`2`(因为$2.828^2≈8$,取整数部分) **解题思路**: - 在区间`[0, n]`中二分查找,找到最大的`mid`满足$mid^2 \leq n$[^3]。 **代码片段**: ```cpp int mySqrt(int n) { if (n <= 1) return n; int left = 0, right = n; while (left <= right) { int mid = left + (right - left) / 2; long long square = (long long)mid * mid; if (square == n) return mid; else if (square < n) left = mid + 1; else right = mid - 1; } return right; // 结束时right是平方根整数部分 } ``` --- #### **4. 统计有序数组中某元素的出现次数** **题目**:在非递减数组中统计`target`出现的次数。 **示例输入**:`nums = [2,4,4,4,6], target = 4` **示例输出**:`3` **解题思路**: 1. 使用两次二分查找分别定位`target`的**第一个位置**和**最后一个位置**。 2. 出现次数为`last_pos - first_pos + 1`[^2]。 **代码片段**: ```cpp int countTarget(vector<int>& nums, int target) { // 查找第一个位置 int first = -1, left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] >= target) right = mid - 1; else left = mid + 1; } first = left; // 查找最后一个位置 left = 0, right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] <= target) left = mid + 1; else right = mid - 1; } int last = right; return (last >= first) ? last - first + 1 : 0; } ``` --- ### §§ 相关问题 § 1. 如何判断二分查找的循环终止条件? 2. 二分查找的时间复杂度为什么是$O(\log n)$? 3. 如何处理旋转有序数组中的二分查找
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值