[算法]漆狗屋/书本分发

漆狗屋

又名:数组分段和最大值最小问题 ,画匠问题,()书本分发看最后面)

题目描述

Description

Dilpreet wants to paint his dog- Buzo’s home that has n boards with different lengths[A1, A2,…, An]. He hired k painters for this work and each painter takes 1 unit time to paint 1 unit of the board.The problem is to find the minimum time to get this job done under the constraints that any painter will only paint continuous sections of boards, say board {2, 3, 4} or only board {1} or nothing but not board {2, 4, 5}.

Constraints:1<=T<=100,1<=k<=30,1<=n<=50,1<=A[i]<=500

Input

The first line consists of a single integer T, the number of test cases. For each test case, the first line contains an integer k denoting the number of painters and integer n denoting the number of boards. Next line contains n- space separated integers denoting the size of boards.

Output

For each test case, the output is an integer displaying the minimum time for painting that house.

Sample Input 1

2
2 4
10 10 10 10
2 4
10 20 30 40

Sample Output 1

20
60
题目解析
  1. 给定一个数组和一个值k,将数组分成k段连续子数组。
  2. 要求这k段子段的和的最大值最小。求出这个值。

eg: 如[10 20 30 40]划分成如2份有

  • [10] [20,30,40] max(10, (20,+30+40)) =90

  • [10,20] [30,40] max((10+20), (30+40)) =70

  • [10,20,30] [40] max((10+20+30), (40)) =60

则ans = min(90, 70,60) = 60

思路解析

求最大值最小问题一般用二分查找解决

请参考这篇文章

此题可以想象成把数据按顺序装入桶中,m即是给定的桶数,问桶的容量至少应该为多少才能恰好把这些数装入k个桶中(按顺序装的)。

  1. 桶容量的最小值是数组的最大值 -->一个数一个桶,桶的数量等于数组的长度

  2. 桶容量的最大值是数组所有元素的和–>所有数放入一个桶,桶的数量等于1

    因此桶容量越大,桶需要的数量就越小

例子:出处

对于数组 1 2 3 4 5 6 7,假设k=3,最小桶容量为7(要5个桶),最大桶容量为28(一个桶)

img

第一行表示桶容量,第二行表示需要的桶数,即要求桶数量恰为k的最小桶容量

注意:在代码实现时,这个表格的第二行是查找到具体位置才通过get_required_painters计算出来的

结论:

对桶容量进行二分查找,计算当前桶容量对应的桶的个数,根据桶的个数决定查找方向 {key:桶容量,value:桶的个数}

代码实现(python)
# 就按照顺序遍历数组累和,如果超过桶容量证明装不下了,桶数量+1,重新换一个新桶
def get_required_painters(arr, cap):
    sum = 0
    numPainters = 1
    for x in arr:
        sum += x
        if sum > cap:
            numPainters += 1
            sum = x
    return numPainters


# 二分查找
# 对桶容量进行二分查找,获取当前桶容量对应的桶的个数,根据桶的个数决定查找方向  {key:桶容量,value:桶的个数}
def search(i, j, arr, k):
    if i == j:
        print(i)
        return
    mid = (i + j) >> 1
    p_num = get_required_painters(arr, mid)  # 以mid为容量的桶,需要多少个才能把数全装完
    if p_num <= k:  # 如果当前的桶的个数比要找的桶的数量小,说明当前桶数量过少,容量过大,因此就向左搜索,
        #  等于也要搜索,因为在桶数量保持不变时,当前容量未必是最小容量
        search(i, mid, arr, k)
    else:
        search(mid + 1, j, arr, k)


if __name__ == '__main__':
    for _ in range(int(input())):
        k = list(map(int, input().split(" ")))[0]
        arr = list(map(int, input().split(" ")))
        min_cap = max(arr)  # 40
        max_cap = sum(arr)  # 100
        search(min_cap, max_cap, arr, k)

书本分发道理是一样的
调整一下输入格式就行了

if __name__ == '__main__':
    for _ in range(int(input())):
        _ = input()
        arr = list(map(int, input().strip().split(" ")))
        k = list(map(int, input().strip().split(" ")))[0]
        min_cap = max(arr)  # 40
        max_cap = sum(arr)  # 100
        if len(arr) < k :
            print(-1)
        else:
            search(min_cap, max_cap, arr, k)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值