Penjadwal Tugas dan Pompa Bensin
Terapkan penalaran greedy pada masalah periode pendinginan penjadwal tugas CPU dan masalah kelayakan pompa bensin melingkar
Penjadwal Tugas dan Pompa Bensin adalah pelajaran DSA Interview Prep gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar DSA Interview Prep, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus DSA Interview Prep mencakup 4 pelajaran total.
Masalah Penjadwal Tugas
Penjadwal Tugas (LeetCode 621): diberikan daftar tugas CPU (masing-masing diberi label A-Z) dan masa jeda n, temukan jumlah minimum interval CPU untuk menyelesaikan semua tugas. Tugas yang sama harus menunggu setidaknya n interval sebelum dijalankan lagi. Interval menganggur diperbolehkan. Untuk tugas ['A','A','A','B','B','B'] dengan masa jeda 2, jawabannya adalah 8: A→B→idle→A→B→idle→A→B.
# Task Scheduler example
tasks = ['A','A','A','B','B','B']
n = 2 # cooldown
# One optimal schedule: A B _ A B _ A B
# Intervals: 1 2 3 4 5 6 7 8 → answer = 8
# Another example: tasks=['A','A','A','B','B','C'] n=2
# A B C A B _ A → 7 intervals
print('Understanding the cooldown constraint')
print('Same task needs n intervals gap between runs')Rumus Serakah untuk Penjadwal Tugas
Wawasan utama: total waktu ditentukan oleh tugas yang paling sering muncul. Jika tugas yang paling sering muncul memiliki frekuensi f dengan jumlah max_count (jumlah tugas dengan frekuensi f), waktunya adalah max(len(tasks), (f-1) * (n+1) + max_count). Rumusnya: buat f-1 kerangka berukuran n+1, isi dengan tugas lain, lalu tambahkan siklus terakhir. Jika tugas lain mengisi semua slot menganggur (banyak tugas yang beragam), cukup jalankan semua tugas tanpa waktu menganggur.
from collections import Counter
def least_interval(tasks, n):
count = Counter(tasks)
max_freq = max(count.values())
# How many tasks have the maximum frequency?
max_count = sum(1 for c in count.values() if c == max_freq)
# Formula: max of total tasks (no idle) or frame-based calculation
frame_time = (max_freq - 1) * (n + 1) + max_count
return max(len(tasks), frame_time)
print(least_interval(['A','A','A','B','B','B'], 2)) # 8
print(least_interval(['A','A','A','B','B','B'], 0)) # 6 (no cooldown)
print(least_interval(['A','A','A','A','B','C'], 3)) # 10Mengapa Rumus Ini Berfungsi
Bayangkan jadwal sebagai kisi dengan n+1 kolom (satu slot tugas + n slot masa jeda). Tugas yang paling sering muncul, A (frekuensi f), memerlukan f baris. Di antara kemunculan pertama dan terakhir, terdapat f-1 kerangka penuh yang masing-masing memiliki n+1 slot. Ditambah kerangka parsial terakhir yang berisi semua tugas dengan frekuensi maksimum. Jika ada cukup banyak tugas yang beragam, tugas-tugas tersebut mengisi semua slot menganggur, dan jumlah tugas sebenarnya melebihi waktu berdasarkan kerangka — gunakan nilai yang lebih besar dari keduanya.
# Visualise frame structure for AAABBB, n=2
# Frame size = n+1 = 3
# f = 3 (A appears 3 times), max_count = 2 (A and B both appear 3 times)
# Grid:
# [A B _] ← frame 1
# [A B _] ← frame 2
# [A B ] ← last partial frame (max_count=2 cells)
# Total = (3-1)*3 + 2 = 6 + 2 = 8
# If tasks = AAAABBCC, n=2: max_freq=4 (A), max_count=1
# (4-1)*(2+1)+1 = 9+1 = 10
# But len(tasks)=8 < 10, so answer is 10
tasks2 = ['A','A','A','A','B','B','C','C']
from collections import Counter
count = Counter(tasks2)
mf = max(count.values())
mc = sum(1 for c in count.values() if c == mf)
print(f'Frame formula: ({mf}-1)*{2+1}+{mc} = {(mf-1)*(2+1)+mc}')
print(f'Max(len={len(tasks2)}, frame={max(len(tasks2),(mf-1)*3+mc)}) = {max(len(tasks2),(mf-1)*3+mc)}')Alternatif Simulasi Berbasis Tumpukan
Simulasi berbasis tumpukan menghasilkan jadwal sebenarnya (bukan sekadar jumlahnya). Pada setiap langkah, ambil tugas tersedia yang paling sering muncul (tumpukan maksimum). Setelah menjalankannya, terapkan masa jeda: jangan masukkan kembali hingga n langkah kemudian. Gunakan antrean untuk melacak tugas yang sedang dalam masa jeda. Ini berjalan dalam O(waktu_total × log k), dengan k adalah jumlah tugas berbeda. Meskipun benar, rumus lebih cepat. Kuasai keduanya — pewawancara mungkin meminta jadwalnya sendiri.
import heapq
from collections import deque, Counter
def task_scheduler_simulate(tasks, n):
count = Counter(tasks)
heap = [-c for c in count.values()] # max-heap using negation
heapq.heapify(heap)
time = 0
cooldown = deque() # (available_at, neg_count)
while heap or cooldown:
time += 1
if heap:
c = heapq.heappop(heap) + 1 # use one instance
if c < 0: # still has remaining tasks
cooldown.append((time + n, c))
if cooldown and cooldown[0][0] == time:
heapq.heappush(heap, cooldown.popleft()[1])
return time
print(task_scheduler_simulate(['A','A','A','B','B','B'], 2)) # 8Masalah Stasiun Bahan Bakar
Stasiun Bahan Bakar (LeetCode 134): terdapat n stasiun bahan bakar dalam sebuah lingkaran. Stasiun i memiliki gas[i] bahan bakar dan memerlukan cost[i] biaya untuk pergi ke stasiun berikutnya. Mulai dengan tangki kosong, temukan stasiun awal yang memungkinkan Anda menyelesaikan putaran. Jika tidak ada stasiun seperti itu, kembalikan -1. Soal ini menjamin paling banyak satu jawaban valid jika jawaban tersebut ada.
# Example:
gas = [1, 2, 3, 4, 5]
cost = [3, 4, 5, 1, 2]
# net gain per station: gas[i] - cost[i]
net = [g - c for g, c in zip(gas, cost)]
print('Net gain per station:', net) # [-2, -2, -2, 3, 3]
# Only possible start: station 3 (index 3)
# Tank: 0 +3=3 → 3-1=2 → 2+1=3-2=... let's verify
print('Sum of net:', sum(net)) # 1 > 0 means solution existsSolusi Serakah untuk Stasiun Bahan Bakar
Algoritme serakah: (1) Jika total bahan bakar < total biaya, solusi tidak ada (kembalikan -1). (2) Jika tidak, tepat satu solusi ada. Temukan dengan satu lintasan: lacak tank (bahan bakar saat ini) dan start (stasiun awal kandidat). Jika tank < 0 setelah mengunjungi sebuah stasiun, start saat ini tidak dapat mencapai stasiun tersebut — atur ulang tank = 0 dan tetapkan start = i + 1. start terakhir adalah jawabannya.
def can_complete_circuit(gas, cost):
if sum(gas) < sum(cost):
return -1 # impossible
tank = 0
start = 0
for i in range(len(gas)):
tank += gas[i] - cost[i]
if tank < 0:
tank = 0
start = i + 1 # current start failed, try next
return start
gas = [1, 2, 3, 4, 5]
cost = [3, 4, 5, 1, 2]
print(can_complete_circuit(gas, cost)) # 3
gas2 = [2, 3, 4]
cost2 = [3, 4, 3]
print(can_complete_circuit(gas2, cost2)) # -1Mengapa Titik Mulai Serakah Benar
Argumen kebenaran: jika tank menjadi negatif setelah mencapai stasiun i dari start, maka tidak ada stasiun antara start dan i (inklusif) yang dapat menjadi titik mulai valid — saat mencapai stasiun i, bahan bakar yang dimiliki semuanya lebih sedikit daripada yang tersedia jika memulai dari start. Jadi, kita dapat melewati semuanya dengan aman dan mencoba i+1. Karena solusi ada (total bahan bakar ≥ total biaya), kandidat start terakhir pasti berhasil.
# Proof sketch: why start=i+1 is correct after tank<0 at station i
# If we start at station j (start <= j <= i), tank at j is tank_from_start(j)
# After stations start..j: tank_from_j starts at 0, but we've already used gas[start..j-1]
# Starting at j means: tank_at_i = sum(net[j..i]) = sum(net[start..i]) - sum(net[start..j-1])
# Since sum(net[start..i]) < 0 AND sum(net[start..j-1]) >= 0 (no reset before i),
# tank_at_i when starting at j is even more negative → j cannot work either
def verify_gas_solution(gas, cost, start):
tank = 0
n = len(gas)
for i in range(n):
idx = (start + i) % n
tank += gas[idx] - cost[idx]
if tank < 0: return False
return True
print(verify_gas_solution([1,2,3,4,5],[3,4,5,1,2], 3)) # TrueMetode Pencarian Menyeluruh vs Serakah untuk Stasiun Bahan Bakar
Metode pencarian menyeluruh mencoba setiap stasiun awal dan menyimulasikan seluruh putaran — waktu O(n²). Solusi serakah satu lintasan membutuhkan waktu O(n) dan ruang O(1). Untuk larik berisi 10⁵ stasiun, perbedaannya adalah 10¹⁰ operasi dibandingkan 10⁵. Sifat matematika utama yang memungkinkan strategi serakah adalah: jika total bahan bakar bersih tidak negatif, titik mulai valid pasti ada, dan titik itu selalu merupakan stasiun tepat setelah titik terakhir ketika jumlah berjalan menjadi negatif.
def brute_force_gas(gas, cost):
n = len(gas)
for start in range(n):
tank = 0
valid = True
for i in range(n):
idx = (start + i) % n
tank += gas[idx] - cost[idx]
if tank < 0: valid = False; break
if valid: return start
return -1
def greedy_gas(gas, cost):
if sum(gas) < sum(cost): return -1
tank = start = 0
for i, (g, c) in enumerate(zip(gas, cost)):
tank += g - c
if tank < 0: tank = 0; start = i + 1
return start
gas = [1,2,3,4,5]; cost = [3,4,5,1,2]
print('Brute:', brute_force_gas(gas,cost), '== Greedy:', greedy_gas(gas,cost))Terkait: Biaya Minimum untuk Menyelesaikan Perjalanan
Waktu Minimum untuk Menyelesaikan Perjalanan (LeetCode 2187) adalah masalah pencarian biner pada ruang jawaban. Anda melakukan pencarian biner pada nilai waktu T: dengan waktu T, bus yang memiliki time[i] menyelesaikan floor(T/time[i]) perjalanan. Jika jumlah perjalanan ≥ totalTrips, T mencukupi. Temukan T minimum tersebut. Ini menunjukkan bahwa strategi rakus dapat diterapkan pada tingkat meta (melakukan pencarian biner atas jawaban) ketika tidak ada aturan rakus langsung pada tingkat objek.
def minimum_time(time, total_trips):
def can_complete(t):
return sum(t // bus for bus in time) >= total_trips
lo, hi = 1, min(time) * total_trips # upper bound
while lo < hi:
mid = (lo + hi) // 2
if can_complete(mid):
hi = mid
else:
lo = mid + 1
return lo
print(minimum_time([1, 2, 3], 5)) # 3 (3/1=3 + 3/2=1 + 3/3=1 = 5)
print(minimum_time([2], 1)) # 2Kasus Tepi dan Verifikasi
Kasus tepi penting untuk kedua masalah: Penjadwal Tugas — ketika masa jeda n=0, jawaban cukup berupa len(tasks) (tidak diperlukan waktu menganggur). Ketika semua tugas sama (misalnya, semuanya 'A'), slot waktu menganggur terisi tepat. Ketika tugas memiliki banyak jenis berbeda, slot waktu menganggur mungkin 0 (tugas mengisi semua kerangka waktu). Stasiun Bahan Bakar — ketika total bahan bakar sama persis dengan total biaya, terdapat tepat satu titik awal yang valid. Ketika satu stasiun memiliki bahan bakar yang cukup untuk seluruh sirkuit, stasiun itulah jawabannya. Selalu verifikasi jawaban algoritma rakus Anda pada kasus-kasus degeneratif ini.
from collections import Counter
def least_interval(tasks, n):
if n == 0: return len(tasks) # no cooldown
cnt = Counter(tasks)
mf = max(cnt.values())
mc = sum(1 for c in cnt.values() if c == mf)
return max(len(tasks), (mf-1)*(n+1)+mc)
# Edge cases for task scheduler
print(least_interval(['A','A','A'], 2)) # 7: A _ _ A _ _ A
print(least_interval(['A','A','B','B'], 0)) # 4: no idle
print(least_interval(['A','B','C','D'], 3)) # 4: all diff, no idle needed
# Edge case for gas station
def gas_station(gas, cost):
if sum(gas) < sum(cost): return -1
tank = start = 0
for i,(g,c) in enumerate(zip(gas,cost)):
tank += g-c
if tank < 0: tank=0; start=i+1
return start
print(gas_station([5,1,2,3,4],[4,4,1,5,1])) # 4Pengenalan Pola Rakus
Penjadwal Tugas dan Stasiun Bahan Bakar sama-sama mengikuti pola rakus: (1) Identifikasi hambatan utama (tugas yang paling sering muncul / keseimbangan bahan bakar bersih). (2) Buat keputusan dalam satu lintasan dengan variabel yang terus diperbarui (max_freq, tank). (3) Mulai ulang atau atur ulang ketika suatu batasan dilanggar. Masalah rakus umum yang perlu Anda ketahui: Pemilihan Aktivitas, Pengodean Huffman, Ransel Pecahan, Permainan Lompatan, Penjadwal Tugas, Stasiun Bahan Bakar, dan Penggabungan Interval. Masing-masing memiliki pembuktian berdasarkan argumen pertukaran atau invarian matematis.
# Greedy pattern summary
# Task Scheduler:
# Bottleneck: max frequency task
# Formula: max(total_tasks, (max_freq-1)*(n+1)+max_count)
# O(n) time, O(1) space
# Gas Station:
# Bottleneck: running sum of (gas-cost) going negative
# Reset start when tank < 0, valid if total sum >= 0
# O(n) time, O(1) space
# Both avoid the need for DP by using a clever single-pass insight
from collections import Counter
def combined_demo(tasks, n, gas, cost):
ti = max(len(tasks), (max(Counter(tasks).values())-1)*(n+1) +
sum(1 for c in Counter(tasks).values() if c==max(Counter(tasks).values())))
tank = start = 0
gs = sum(g-c for g,c in zip(gas,cost)) >= 0
return ti, start if gs else -1Periksa Cepat
Uji pemahaman Anda terhadap konsep Struktur Data & Algoritma — Persiapan Wawancara Pemrograman dari pelajaran ini.
Ringkasan Pelajaran
Dalam pelajaran ini, Anda mempelajari: jawaban Penjadwal Tugas = max(total_tasks, (max_freq-1)*(n+1)+max_count) — diturunkan dengan mengisi kisi berbasis kerangka menggunakan tugas yang paling sering muncul, Stasiun Bahan Bakar menggunakan satu lintasan, dengan mengatur ulang start=i+1 setiap kali tank bernilai negatif, dan valid jika total bahan bakar ≥ total biaya, serta kedua masalah menggunakan waktu O(n) dan ruang O(1) dengan mengidentifikasi invarian matematis, bukan melalui pencarian menyeluruh. Berikutnya, kita akan mempelajari templat Pecah dan Taklukkan serta penerapannya di luar pengurutan merge.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Penjadwal Tugas dan Pompa Bensin” gratis?
Ya — teks lengkap “Penjadwal Tugas dan Pompa Bensin” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus DSA Interview Prep, upgrade ke CoddyKit PRO. Kursus DSA Interview Prep mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Penjadwal Tugas dan Pompa Bensin”?
Terapkan penalaran greedy pada masalah periode pendinginan penjadwal tugas CPU dan masalah kelayakan pompa bensin melingkar Kamu berlatih DSA Interview Prep dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai DSA Interview Prep?
Tidak diperlukan pengalaman sebelumnya. DSA Interview Prep di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.
Berapa lama pelajaran “Penjadwal Tugas dan Pompa Bensin” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran DSA Interview Prep ini?
Ya. Setiap pelajaran DSA Interview Prep menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Greedy vs DP: Kapan Menggunakan Masing-Masing
- Penjadwalan dan Penggabungan Interval
- Jump Game I dan II
- Penjadwal Tugas dan Pompa Bensin