C++で解くジョブスケジュールの最小難易度問題
問題概要
d日間でタスクのリストをスケジューリングすることを考えます。タスクには依存関係があり、i番目のタスクに取り掛かるためには、0 <= j < i を満たすすべてのタスク j を先に完了させておく必要があります。
さらに、毎日最低1つはタスクを完了させなければなりません。スケジュール全体の難易度は、d日間の各日の難易度の合計として定義され、ある日の難易度は、その日に完了したタスクの中で最も高い難易度の値となります。
ここで、整数型配列 taskDifficulty と整数 d が与えられます。i番目のタスクの難易度は taskDifficulty[i] です。スケジュール全体の難易度の最小値を求めてください。スケジュールが構成できない場合は -1 を返します。
入力例と出力例
たとえば、taskDifficulty = [6,5,4,3,2,1]、d = 2 が入力された場合を考えてみましょう。
このとき出力は 7 になります。1日目に最初の5つのタスクを処理すると、その日の難易度は区間の最大値である 6。2日目に残りの1つのタスクを処理すると、難易度は 1 です。したがって、スケジュール全体の難易度は 6 + 1 = 7 となります。
解法のアプローチ
この問題は、動的計画法(DP)とメモ化再帰を組み合わせることで効率的に解けます。手順は以下の通りです。
- solve() 関数を定義します。引数は配列 v、現在のインデックス idx、残り日数 k、二次元配列 dp です。
- idx が配列 v のサイズと等しく、かつ k が 0 の場合は 0 を返します(全タスクを指定日数内で完了できた状態)。
- k < 0 の場合、または idx が配列サイズに達していて k > 0 の場合は、大きな値(1e6)を返します(無効な状態)。
- dp[idx][k] が -1 以外の場合、すでに計算済みなのでその値を返します。
- maxVal を 0、ret を INT_MAX で初期化します。
- i を idx から配列末尾までループさせます。
- maxVal を v[i] との最大値で更新します(idx〜i 区間の最大難易度を保持)。
- ret を「maxVal + solve(v, i + 1, k - 1, dp)」との最小値で更新します。
- 結果を dp[idx][k] に記録して返します。
メイン関数での処理
- n をタスクの総数とします。
- d > n の場合、毎日1つ以上のタスクを処理する条件を満たせないため -1 を返します。
- サイズ n × (d + 1) の二次元配列 dp を作成し、すべて -1 で初期化します。
- solve(j, 0, d, dp) の結果を返します。
このアルゴリズムの時間計算量は O(n² × d)、空間計算量は O(n × d) です。
C++実装例
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int solve(vector<int>& v, int idx, int k, vector<vector<int> >&
dp){
if (idx == v.size() && k == 0)
return 0;
if (k < 0 || idx == v.size() && k > 0)
return 1e6;
if (dp[idx][k] != -1)
return dp[idx][k];
int maxVal = 0;
int ret = INT_MAX;
for (int i = idx; i < v.size(); i++) {
maxVal = max(v[i], maxVal);
ret = min(ret, maxVal + solve(v, i + 1, k - 1, dp));
}
return dp[idx][k] = ret;
}
int minDifficulty(vector<int>& j, int d){
int n = j.size();
if (d > n)
return -1;
vector<vector<int> > dp(n, vector<int>(d + 1, -1));
return solve(j, 0, d, dp);
}
};
main(){
Solution ob;
vector<int> v = {6,5,4,3,2,1};
cout << (ob.minDifficulty(v, 2));
}
入力
{6,5,4,3,2,1}, 2出力
7
-
C++で最も視聴された上位k番組の合計視聴時間を求める方法
テレビ番組のリストと、それぞれの視聴時間のリスト、さらに整数 k が与えられたとします。shows[i] と duration[i] は、i 番目の人が視聴した番組名とその視聴時間を表しています。このとき、最も視聴時間の長い上位 k 個の番組の合計視聴時間を求めるのが本記事の目的です。問題の例例えば、入力が以下のような場合を考えてみましょう。shows: [Castle Play, Fairy Tale Series, Castle Play, Jerry Mouse, Rich Boy]duration: [6, 4, 6, 14, 5]k = 2この場合の出力は 26 になります。理由を見
-
C++で解くジョブスケジューリング問題:重複しないタスク選択による最大利益の求め方
問題の概要n個の異なるタスクがあるとします。各タスクiは startTime[i] から endTime[i] まで実行され、完了すると profit[i] の利益が得られます。startTime・endTime・profit の3つのリストが与えられたとき、実行時間帯が互いに重ならないようなタスクの部分集合の中で、得られる利益の合計が最大になる値を求めてください。なお、あるタスクが時刻Xに終了する場合、同じ時刻Xに開始する別のタスクを選ぶことは可能です(終了時刻と開始時刻が一致していても重複とはみなしません)。入力例startTime = [1,2,3,3]、endTime = [3,4,5