Pythonで同種タスク間にk時間の間隔を空けて全タスクを完了するのに必要な最小時間を求めるプログラム
問題の概要
整数のリスト tasks(各要素はタスクの種類を表します)と、負でない整数 k が与えられます。各タスクの完了には1単位の時間がかかり、タスクは必ず与えられた順序どおりに実行しなければなりません。ただし、同じ種類のタスクを2回実行する間には、少なくとも k 単位の時間を空ける必要があります。任意の時点で選択できるのは「タスクを実行する」か「待機する」かのいずれかです。すべてのタスクを完了するまでに必要な合計時間を求めてください。
入力例
たとえば、tasks = [0, 1, 1, 2]、k = 2 の場合、出力は 6 になります。
最初の2つのタスクは種類が異なるため、間隔を空けずに連続して実行できます。しかし時刻2で次のタスクは1番目と同じ種類なので、2タイムスロット待機してから実行し、最後に残りの別種類のタスク(タイプ2)を実行します。つまり実行の流れは次のようになります。
[0, 1, 待機, 待機, 1, 2]
このことから、必要なタイムスロットの総数は6であることがわかります。
解法のアプローチ
この問題は、タスクの種類ごとに「次に実行可能になる時刻」を記録しておくことで、効率よく解くことができます。手順は以下のとおりです。
- 現在時刻を表す
tickを0で初期化します。 - タスク種類ごとの次回実行可能時刻を格納するマップ
slotを用意します。 - リスト内の各タスク
tに対して、以下を繰り返します。tがslotに登録されていれば、その値をtfとします。tfが存在し、かつtf - tick > 0の場合は、tickをtfまで進めます(クールダウン時間ぶん待機)。tickを1増やしてタスクを実行します。slot[t] = tick + kとして、この種類のタスクを次に実行できる最早時刻を更新します。
- 最後に
tickを返します。
Pythonでの実装例
理解を深めるために、以下の実装例を見てみましょう。
def solve(tasks, k):
tick = 0
slot = {}
for t in tasks:
tf = slot.get(t)
if tf is not None and tf - tick > 0:
tick += tf - tick
tick += 1
slot[t] = tick + k
return tick
tasks = [0, 1, 1, 2]
k = 2
print(solve(tasks, k))
入力
[0, 1, 1, 2]
出力
6
計算量
タスクの総数を n とすると、リストを一度走査するだけで処理が完了するため、時間計算量は O(n) です。また、登場するタスクの種類数を m とすると、マップに保存する情報は種類ごとに1件のみなので、空間計算量は O(m) となります。
-
Pythonで完了できるタスク数を求めるプログラムの書き方
問題の概要タスクのリストと人のリストが与えられたとします。tasks[i] は i 番目のタスクを実行するために必要な体力(強さ)を表し、people[i] は i 番目の人が持っている体力を表します。ここで、「1人は最大でも1つのタスクしか担当できない」という条件のもとで、完了できるタスクの総数を求める必要があります。例えば、入力が tasks = [4, 3, 9, 15]、people = [10, 5, 3, 2] の場合、出力は 3 になります。これは、1人目がタスク「9」を、2人目がタスク「4」を、3人目がタスク「3」をそれぞれ実行でき、4人目はどのタスクも実行できないためです。解
-
Pythonで1つの数を別の数に変換するのに必要な最小操作回数を求めるプログラム
問題の概要 2つの整数 start と end(start < end)が与えられます。次の2種類の操作のみを使って start を end に変換するとき、必要な操作の最小回数を求めるプログラムを作成しましょう。 数値に 1 を加える(インクリメント) 数値に 2 を掛ける 例として、start = 5、end = 11 の場合を考えます。5 に 2 を掛けて 10 とし、そこへ 1 を加えれば 11 になるため、答えは 2 回となります。 解き方のアプローチ この問題は、start から順に操作を試すよりも、end から逆算していく貪欲法(グリーディ法)が有効です。end が偶