🐙

NeetCode 150 [Binary Search]:medium 2/5

に公開

NeetCodeのSolutionを書いていく

Koko Eating Bananas

問題概要

整数の配列pilesが与えられます。
piles[i]はi番目の山のバナナの数です。
整数hも与えられ、hは全てのバナナを食べきらないといけない時間です。

バナナを食べる時速kを決めます。
毎時間山を決めて、k個のバナナをその山から食べます。
山のバナナがk個以下であれば食べ切れますが、食べ終わっても移動はできません。

h時間以内にバナナを食べきる最小のkを返しなさい。

Input: piles = [1,4,3,2], h = 9
Output: 2

Input: piles = [25,10,23,4], h = 4
Output: 25

制約

  • pilesのサイズは1以上1000以下
  • hはpiles.length以上1,000,000以下
  • piles[i]は1以上1,000,000,000以下

計算:O(nlogm) nは配列数。mは配列の最大値。
メモリ:O(1)

ヒント1

  • hは常にpiles.lengthより大きいです
  • 回答(k)の上限はわかりますか?
  • xのバナナの山を食べるにはどれくらい時間がかかりますか?

kの上限は、pilesの山の最大値?piles = [1,4,3,2]だったら4?
xのバナナの山を食べるには -(-x/k)の時間がかかる。(切り上げ除算)

ヒント2

  • h時間以内にバナナを食べ終わらなくてはなりません。
  • kの上限値はわかりますか?

ヒント1との違いがわからん。。。

ヒント3

  • kの上限値はpilesの最大値です。Kokoが最大の山を1時間で食べられるならその他の山も1時間で食べられるからです

ヒント1の時点でその様に思っていたけども、だいぶ手厚いヒントだったんですね。

ヒント4

  • pilesの最大値をm、pilesの長さをnとすると、強引にやると1~mの数字でkと時間を確認すればできますがO(n*m)の時間がかかります。
  • 効率的にできますか?検索アルゴリズムが役に立つはずです。

そうか!1~mの数字をバイナリサーチ的に使えばよいのか!
つまり、k=m/2から調査を開始してh以下に収まっていればk=m/2/2を評価対象にする、そうでなければk=m*3/4を評価対象にする。
評価を繰り返してkの最小値を返せばOK!

from typing import List


class Solution:
    def minEatingSpeed(self, piles: List[int], h: int) -> int:
        def ceil(x, y):
            return -(-x // y)

        def get_time(k):
            v = 0
            for pile in piles:
                v += ceil(pile, k)
            return v

        # 戻り値kの上限値はpipesの最大値
        m = max(piles)

        # 1~mの中で最小のkを探す
        result = m
        min_k = 1
        max_k = m
        while True:
            k = (min_k + max_k) // 2
            if get_time(k) <= h:
                if result > k:
                    result = k
                if max_k == k:
                    break
                max_k = k
            else:
                if min_k == k:
                    break
                min_k = k

        return result

Discussion