Muz yeme hızı
Problem
Önünde muz yığınları duran bir maymun var; her yığındaki muz sayısı bir dizi olarak veriliyor ve toplam h saati var (h, yığın sayısından az değil). Maymun saatte k muz yeme hızı seçiyor. Her saat tek bir yığından en fazla k muz yer; yığında k'dan az muz kaldıysa onları bitirir ve o saatin kalanında başka yığına geçmez. Tüm muzları h saat içinde bitirebileceği en küçük tamsayı k nedir?
Girdi: piles = [9, 4, 15, 7], h = 7
Çıktı: 7
k = 7: 2 + 1 + 3 + 1 = 7 saat (yetişir)
k = 6: 2 + 1 + 3 + 2 = 8 saat (yetişmez)
Girdi: piles = [9, 4, 15, 7], h = 4
Çıktı: 15 (her yığına tam bir saat)
Netleştirme soruları
h >= yığın sayısıgaranti mi? (Değilse hiçbir hız yetmez; ne dönmeli?)- Yığın boyutları ve
hne kadar büyük? (10⁹; hız aralığı ve taşma riski.) - Hız tamsayı mı olmalı? (Evet.)
- Boş yığın (0 muz) olabilir mi?
İpuçları
İpucu 1
Belirli bir hız k verilse, kaç saatte bitirdiğini O(n)'de hesaplayabilir misin? Her yığın için ceil(pile / k) saat.
İpucu 2
Hız arttıkça gereken saat hiç artmaz (monoton). "Yetişiyor mu?" sorusunun cevabı hız ekseninde hayır, hayır, ..., evet, evet şeklindedir.
İpucu 3
Hız 1 ile max(piles) arasında. Bu aralıkta "yetişen ilk hızı" binary search ile ara (lower bound). Saat toplamını long tut.
Yaklaşım
Brute force: k = 1, 2, 3, ... diye dene, ilk yetişeni döndür. Her deneme O(n); en kötü durumda max(piles) kadar deneme: O(n × max). Değerler 10⁹ olunca imkansız.
Cevap üzerinde binary search: Bu sorunun özü, aradığımız değerin bir dizi elemanı değil, bir parametre olması. İki koşul binary search'ü mümkün kılar:
- Arama aralığı sınırlı: En küçük anlamlı hız 1; en büyük gerekli hız
max(piles), çünkü bu hızla her yığın tek saatte biter ve daha hızlı yemek hiçbir şeyi değiştirmez (h >= nolduğu için yetişir). - Yüklem monoton:
hoursNeeded(k)artmayan bir fonksiyon.kiçin yetişiyorsak+1için de yetişir.
Böylece "yetişen en küçük k" bir lower bound problemidir. Şablon: lo < hi döngüsü, mid yetişiyorsa hi = mid (mid aday olabilir, atma), yetişmiyorsa lo = mid + 1. Döngü bittiğinde lo == hi cevaptır. Bu şablonun avantajı, "son bulunanı ayrı değişkende tut" gerektirmemesi ve sonsuz döngüye girmemesi (mid her zaman hi'den küçük).
İki taşma tuzağı:
hoursNeededtoplamı:k = 1iken 10⁴ yığın × 10⁹ muz = 10¹³ saat; int'e sığmaz,longgerekir. Toplam taşarsa negatif olur ve "yetişiyor" sanılır.- Tavan bölmesi:
(pile + k - 1) / kLeetCode sınırlarında sığar ama değerler int sınırına yaklaşırsa taşar.(pile - 1) / k + 1her pozitifpileiçin güvenlidir.
Java çözümü
public final class KokoEatingBananas {
public int minEatingSpeed(int[] piles, int hours) {
int lo = 1, hi = 0;
for (int pile : piles) {
hi = Math.max(hi, pile); // bu hızla her yığın 1 saatte biter
}
while (lo < hi) { // "yetişen en küçük hız" = lower bound
int mid = lo + (hi - lo) / 2;
if (hoursNeeded(piles, mid) <= hours) {
hi = mid; // mid yetiyor; daha küçüğü de olabilir
} else {
lo = mid + 1;
}
}
return lo;
}
private static long hoursNeeded(int[] piles, int speed) {
long total = 0; // speed = 1 iken toplam int'e sığmayabilir
for (int pile : piles) {
total += (pile - 1) / speed + 1; // ceil(pile / speed), taşmadan
}
return total;
}
}
Karmaşıklık
- Zaman: O(n × log M); M =
max(piles). Binary search ~30 adım (log₂ 10⁹), her adımda O(n) kontrol. - Bellek: O(1).
- Alt sınırı
ceil(toplam / h)ile başlatmak adım sayısını biraz azaltır ama asimptotik değişmez; toplamı hesaplarken yinelonggerekir.
Kenar durumlar ve testler
main içinde derlenip çalıştırıldı; 2.000 rastgele girdide (1-8 yığın, 1-30 muz, h n ile n+39 arası) k = 1'den yukarı deneyen brute force ile karşılaştırıldı.
| Durum | Girdi, h | Beklenen | Neyi yakalar |
|---|---|---|---|
| Temel | [9,4,15,7], 7 | 7 | Ana akış |
| h = n | [9,4,15,7], 4 | 15 | Üst sınır = max |
| Bol zaman | [9,4,15,7], 35 | 1 | Alt sınır |
| Tek küçük yığın | [1], 1 | 1 | |
| Büyük yığın | [10⁹], 2 | 5×10⁸ | |
| İki büyük yığın | [10⁹, 10⁹], 2 | 10⁹ | Tavan bölmesi |
| Saat toplamı taşması | 10⁴ × [10⁹], 10⁹ | 10.000 | long toplam |
Mülakatçı için
Bu soru, adayın "cevap üzerinde binary search" kalıbını tanıyıp tanımadığını ölçer; backend'de karşılığı kapasite planlamasıdır ("günde en az kaç sipariş işlersek 5 günde biter?"). Monotonluğu açıkça söylemek beklenir: binary search'ün sıralı dizi değil monoton yüklem gerektirdiğini anlatabilen aday kalıbı gerçekten anlamıştır.
İpucu zamanlaması: Aday hızları lineer denemeye başlarsa, kaç deneme gerekebileceğini sor (10⁹). Hâlâ bulamazsa ikinci ipucunu ver. Taşma hatasını kendisi görmezse kod bittikten sonra "k = 1 ve 10⁴ tane 10⁹'luk yığında toplam kaç?" diye sor. Lower bound döngüsünde hi = mid - 1 gibi bir sınır hatası yaparsa [9,4,15,7], 7 ile yürütmesini iste.
h >= ngarantisini ve değer aralıklarını netleştirdi.- Verilen hız için gereken saati O(n)'de hesaplayan yardımcıyı ayırdı.
- Yüklemin monoton olduğunu açıkça söyledi ve arama aralığını (1..max) gerekçelendirdi.
- Lower bound şablonunu sınır hatası olmadan yazdı.
- Saat toplamındaki taşmayı fark edip
longkullandı. - Tavan bölmesini kayan nokta kullanmadan yaptı (
Math.ceil((double) ...)hassasiyet ve hız açısından gereksiz). - Karmaşıklığı O(n log M) olarak söyledi.
Takip soruları:
- Paketler sırayla gönderilecek ve günlük kapasite en az ne olmalı ki
Dgünde bitsin? (Beklenen: aynı kalıp, aralıkmax..toplam; LeetCode 1011.) - Diziyi
mardışık parçaya bölüp en büyük parça toplamını minimize et. (Beklenen: aynı kalıp; LeetCode 410.) - Bu bir batch job:
ndosya, her biri farklı boyutta,hsaat SLA var; worker throughput'unu (MB/saat) nasıl seçersin ve bu modelde neyi gözden kaçırıyoruz? (Beklenen: aynı binary search; ama gerçekte paralellik, dosya başına sabit maliyet ve throughput'un sabit olmaması.)
Benzer problemler
- Capacity To Ship Packages Within D Days (LeetCode 1011).
- Split Array Largest Sum (LeetCode 410).
- Minimum Number of Days to Make m Bouquets (LeetCode 1482).
- Search in Rotated Sorted Array (LeetCode 33) (bu sette:
search-in-rotated-sorted-array).