每周一课:L14 Binary search algorithm

作者: AiFany | 来源:发表于2019-02-19 09:36 被阅读1次
0.png

P14.1 NailingPlanks
Count the minimum number of nails that allow a series of planks to be nailed.

  • P14.1 钉木板
    计算钉牢所有木板需要的钉子的最小数量

两个非空数组A和B,均由N个整数组成,这些数组表示N个木板。更准确地说,A[K]是第K个木板的起点,B[K]是终点。

一个由M个整数组成的非空数组C,这个数组表示M个钉子。更准确地说,C[I]是可以钉入第I个钉子的位置。

如果存在钉子c[I],对于任意的K,只要满足A[K] ≤ C[I] ≤ B[K],就表示木板(A[K], ****B[K])可以被钉牢。

目标是找到使得所有木板都被钉牢,所使用的最小钉子数量。换句话说,应该找到一个值J,这样在只使用前J个钉子之后所有木板都被钉牢了。也就是说,对于每一块木板(A[K], B[K]),其中0 ≤ K < N,应该存在一个钉子C[I] ,使得I < J和A[K] ≤ C[I] ≤ B[K]成立。

例如,给定的数组A、B:

A[0]=1,A[1]=4,A[2]=5,A[3]=8;
B[0]=4,B[1]=5,B[2]=9,B[3]=10
四块木板分别为[1,4]、[4,5]、[5,9]和[8,10]。

给定的数组C:
C[0]=4,C[1]=6,C[2]=7,C[3]=10,C[4]=2

使用以下钉子:

  1. 使用第1个钉子:[1,4]和[4,5]都被钉牢:因为1<=4<=4,4<=4<=5。
  2. 使用前2个钉子:木板[1,4],[4,5]和[5,9]被钉牢。
  3. 使用前3个钉子:木板[1、4]、[4、5]和[5、9]被钉牢,
  4. 使用前4个钉子:所有的木板都将被钉牢。

因此,4是使用的最小钉子的数量,可以钉牢所有木板。

编写函数:

def solution(A, B, C)

给定两个由N个整数组成的非空数组A和B,一个由M个整数组成的非空数组C,则返回钉牢所有木板需要使用的最小的钉子数量。如果无法钉牢所有木板,函数应返回-1。

例如,针对上面的例子,函数应返回4。

假定:

  1. N和M是区间[1,30000]内的整数;
  2. 数组A、B、C的每个元素都是区间[1,2*M]内的整数;
  3. A[K] ≤ B[K];
  • 解题思路

用二分法查找,每个木板可以被钉牢需要的钉子数,然后在可以被钉牢的集合中寻找最小的钉子数。也就为每个木板寻找最早的可以钉牢该木板的钉子的位置。最后遍历木板,获取最大的钉子数,即为所求。

  • Python3代码
# -*- coding:utf-8 -*-
# &Author  AnFany
# Lesson 14:Binary search algorithm
# P 14.1 NailingPlanks


def solution_worst(A, B, C):
    """
    数组A、B对应元素代表木板,C元素代表可以钉钉子的位置。计算钉牢所有木板需要的最小的钉子数量。
    一般解法:时间复杂度O((N + M) * N)
    :param A: 正整数数组
    :param B: 正整数数组
    :param C: 正整数数组
    :return: 最小的钉子数量
    """
    D = [0] * len(A)
    for index, value in enumerate(C):
        for i, j in enumerate(A):
            if j <= value <= B[i]:
                D[i] = 1
        if 0 not in D:
            return index + 1
    return -1


def solution_worse(A, B, C):
    """
    数组A、B对应元素代表木板,C元素代表可以钉钉子的位置。计算钉牢所有木板需要的最小的钉子数量。
    一般解法:时间复杂度O(N * M)
    :param A: 正整数数组
    :param B: 正整数数组
    :param C: 正整数数组
    :return: 最小的钉子数量
    """
    for index, value in enumerate(C):
        if A:
            DA = []
            DB = []
            for i, j in enumerate(A):  # 这一步可以进行二分查找优化
                if j > value or value > B[i]:
                    DA.append(j)
                    DB.append(B[i])
            A = DA.copy()
            B = DB.copy()
            if not A:
                return index + 1
    return -1


def binary_search(nails, start, end, compare_num):
    """
    为每个木板计算可以钉牢该木板的第一个出现的钉子位置
    :param nails: 按照钉子可以钉的位置顺序排列的二位数组
    :param start: 木板的起始点
    :param end: 木板的终点
    :param compare_num: 之前的所有木板需要的最小的钉子数
    :return: 第一次可以被钉子钉牢的钉子的位置
    """
    length = len(nails)
    # 进行二分查找法
    b_s_start = 0
    b_s_end = length - 1
    nailed_position = -1
    search_min = 0
    while b_s_start <= b_s_end:

        b_s_middle = int((b_s_start + b_s_end) / 2)
        number = nails[b_s_middle][1]

        if number < start:  # 钉子位置小于木板起始点
            b_s_start = b_s_middle + 1

        elif number > end:  # 钉子位置大于木板终点
            b_s_end = b_s_middle - 1

        else:  # 可以被钉牢
            nailed_position = nails[b_s_middle][0]  # 钉子序列中的前nailed_position个钉子,可以将这个木板钉牢
            # 但是b_s_middle并不一定是最小的,导致nailed_position也不一定是最小的。b_s_end需要继续减小
            b_s_end = b_s_middle - 1
            search_min = b_s_middle

    if nailed_position == -1:  # 这个木板不能被钉牢
        return -1
    #  下面从最小的可以钉牢该木板的b_s_middle开始,寻找最小的nailed_position数。
    else:
        search_min += 1  # 因为b_s_middle处的钉子的个数值为nailed_position
        while search_min < length:
            if nails[search_min][1] > end:  # 之后的钉子都不可能钉牢该木板
                break
            # 如果可以钉牢,则选择较小的nailed_position,也就是按顺序的钉子的数最小
            nailed_position = min(nailed_position, nails[search_min][0])
            
            if nailed_position <= compare_num:  # 小于之前木板所需要的钉子数可以钉牢该木板的话,就停止计算即可
                return compare_num
            
            search_min += 1  # 继续搜索,寻找最小的nailed_position
            
        return max(nailed_position, compare_num)  # 返回


def solution(A, B, C):
    """
    数组A、B对应元素代表木板,C元素代表可以钉钉子的位置。计算钉牢所有木板需要的最小的钉子数量。
    二分法查找:时间复杂度O((N + M) * log(M))
    :param A: 正整数数组
    :param B: 正整数数组
    :param C: 正整数数组
    :return: 最小的钉子数量
    """
    nails_c = sorted(enumerate(C), key=lambda x: x[1])  # 按照值的大小排列,索引是无序的。索引代表着前几个钉子
    result = -1
    for i in range(len(A)):
        result = binary_search(nails_c, A[i], B[i], result)
        if result == -1:
            return -1
    return result + 1  # 因为索引从0开始,因此需要加1
  • 结果
image

点击获得更多编程练习题。欢迎Follow,感谢Star!!! 扫描关注微信公众号pythonfan,获取更多。

image image

相关文章

网友评论

    本文标题:每周一课:L14 Binary search algorithm

    本文链接:https://www.haomeiwen.com/subject/zvxxyqtx.html