1.枚举查找和二分查找的区别
枚举查找也就是顺序查找。
实现原理就是逐个比较al0.n-11中的元素,直到找出元素x或将索偏整个数组后确定义不在其中,或者说符合要求的元素在不在数组中。
最坏的情况下需要比较N次,时间复杂度是O(n)线性阶。
二分查找也就是折半查找。折半查找是将N个元素分成大致相同的两部分。选取中间元素与查找签的元素比较,或者与查找条件相比较,找到或者说找到下一次查找的半区。每次都将范围缩小至;所以时间复杂度是0(log2n),但是二分查找的前提是有序的,一般是从小到排列。折半查找的基本思想:
在有序表中(low,high,low<=high),取中间记录即[(high+low)/2]作为比较对象。
●若给定值与中间记录的关键码相等,则查找成功
● 若给定值小于中间记录的关键码,则在中间记录的左半区继续查找
● 若给定值大于中间记录的关键码,则在中间记录的右半区继续查找
不断重复上述过程,直到查找成功,或所查找的区域无记录,查找失败。
二分查找的特征:
1.答案具有单调性;
2.二分答案的问题往往有固定的问法,比如:令最大值最小(最小值最大),求满足条件的最大(小)值等。
模板:
#在单调递增序列a中查找>=x的数中最小的一个(后推x)
while low < high:
mid = (low + high) / 2
if a[mid] >= x:
high = mid
else:
low = mid + 1
#在单调递增序列a中查找<=x的数中最大的一个(前推x)
while low < high:
mid = (low + high) / 2
if a[mid] <= x:
low = mid
else:
high = mid - 1
题目:跳石头
题目描述
一年一度的"跳石头"比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M 块岩石(不能移走起点和终点的岩石)。
输入描述
输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。
接下来 N 行,每行一个整数,第 i 行的整数 Di(0<Di<L)表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
其中,0≤M≤N≤5×10^4,1≤L≤10^9。
输出描述
输出只包含一个整数,即最短跳跃距离的最大值。
输入输出样例
示例
输入
25 5 2
2
11
14
17
21
输出
4
二分法套路题:最小值最大化、最大值最小化
在n块岩石中移走m个石头,有很多种移动方法。 在第i种移动方法中,剩下的石头之间的距离,有一个最小距离ai。 在所有移动方法的最小距离ai中,问最大的ai是多少。 在所有可能的最小值中,找最大的那个,就是“最小值最大化”。
二分思路:不找搬走石头的各种组合,而是给出一个距离d,检查能不能搬走m块石头而得到最短距离d。把所有的d都试一遍,肯定能找到一个最短的d。用二分法找这个d。
import os
import sys
L, N, M = map(int, input().split())
stone = [] # 石头i和到起起点的距离
for i in range(N):
t = int(input())
stone.append(t)
# 思路是能不能搬走某m个石头,得到距离d
def check(d):
num = 0
pos = 0 # pos=0 意味着第一次
# 从头到尾搬石头 如果
for i in range(0,N): # 右是开区间,所以我从0到n-1作为石头下标
if (stone[i]-pos < d): # 第i块可以搬走
num += 1 #
else:
pos = stone[i]
if num <= M: return True
else: return False
l, r = 1, L
while (l<r):
mid = l+(r-l)//2
if check(mid):
l = mid+1
else:
r = mid-1
# 现在l可能符合 也可能大1 所以如果l不符合,l-=1 符合就直接输出
if check(l):
print(l)
else:
print(l-1)
题目:一元三次方程
题目描述
有形如:ax^3+bx^2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在 −100 至 100 之间),且根与根之差的绝对值 ≥1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 2 位。
提示:记方程f(x)=0,若存在 2 个数 x1 和 x2,且x1<x2,f(x1)×f(x2)<0,则在 (x1,x2)之间一定有一个根。
输入描述
输入一行,4 个实数a,b,c,d。
输出描述
输出一行,3 个实根,从小到大输出,并精确到小数点后 2 位。
输入输出样例
示例 1
输入
1 -5 -4 20
输出
-2.00 2.00 5.00
题解:
def y(x): #复制函数
return a*x*x*x+b*x*x+c*x+d
a,b,c,d = map(float,input().split())
for i in range(-100,100):
left = i #left 和 right相当于x1和x2
right = i+1
y1 = y(left)
y2 = y(right)
if y1 == 0: #令f(x)=0
print("{:.2f}".format(left),end=' ')
if y1*y2<0: #f(x1)*f(x2)<0
while right-left>=0.001:
mid = (left+right)/2
if y(mid)*y(right)<=0:
left = mid
else:
right = mid
print("{:.2f}".format(right),end=' ')
题目:分巧克力
题目描述
儿童节那天有 K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有 N 块巧克力,其中第 i 块是 Hi×Wi 的方格组成的长方形。为了公平起见,
小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。切出的巧克力需要满足:
-
形状是正方形,边长是整数;
-
大小相同;
例如一块 6x5 的巧克力可以切出 6 块 2x2 的巧克力或者 2 块 3x3 的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?
输入描述
第一行包含两个整数N,K (1≤N,K≤10^5)。
以下 N 行每行包含两个整数 Hi,Wi (1≤Hi,Wi≤10^5)。
输入保证每位小朋友至少能获得一块 1x1 的巧克力。
输出描述
输出切出的正方形巧克力最大可能的边长。
输入输出样例
示例
输入
2 10
6 5
5 6
输出
2
题解:
1.设立一个检查函数,检查每块巧克力在规定大小情况下取得的最多数量
公式:最多数量=(巧克力长度 // 需要长度)* (巧克力宽度 // 需要长度)
拿这个最多数量与小朋友人数k进行比较
2.利用二分查找找到能满足每个小朋友都能分到巧克力的最大长度
n, k = map(int, input().split())
chocolate = [list(map(int,input().split())) for _ in range(n)]
front, tail = 1, 100000
def find(edge_len):
global k
ans = 0
for wid, len in chocolate:
ans += (wid//edge_len)*(len//edge_len)
if ans>=k:
return True
return False
while front<=tail:
mid = (front + tail)//2
if not find(mid):
tail = mid - 1
else:
front = mid + 1
print(tail)