[開始時刻、終了時刻、コスト]のn個のジョブのセットを指定して、2つのジョブが重複せず、コストが最大になるようにサブセットを見つけます。
今、私は貪欲なアルゴリズムがうまくいくかどうかわかりません。つまり、コストで並べ替え、常に交差せず、2つの間の最大コストで次のジョブを実行します。
これはナップザック問題と同等ですか?どうすればそれにアプローチできますか?
[開始時刻、終了時刻、コスト]のn個のジョブのセットを指定して、2つのジョブが重複せず、コストが最大になるようにサブセットを見つけます。
今、私は貪欲なアルゴリズムがうまくいくかどうかわかりません。つまり、コストで並べ替え、常に交差せず、2つの間の最大コストで次のジョブを実行します。
これはナップザック問題と同等ですか?どうすればそれにアプローチできますか?
回答:
その場合、貪欲なアルゴリズムは役に立ちません。また、フラクショナルまたは0-1ナップザック問題と比較することはできませんでした。最初はO(n)の貪欲なアルゴリズムによって解決でき、2番目はNPです。
あなたが持っている問題は、O(2 ^ n)で総当たりする可能性があります。しかし、動的プログラミングを使用して最適化できます。
1)間隔を開始時間でソートします。
2)int []コスト=新しいint [jobs.length]をInteger.MIN_VALUE(または任意の負の値)で初期化します。
3)次の再帰ルーチンを定義します(ここにJavaがあります):
private int findCost(Job[] jobs, int k, int[] costs) {
if(k >= jobs.length) {
return 0;
}
if(costs[k] < 0) {
int x = findNextCompatibleJob(jobs, k);
int sumK = jobs[k].cost + findCost(jobs, x, costs);
int sumK1 = findCost(jobs, k + 1, costs);
costs[k] = Math.max(sumK, sumK1);
}
return costs[k];
}
private int findNextCompatibleJob(Job[] jobs, int k) {
int finish = jobs[k].finish;
for(int i = k + 1; i < jobs.length; i++) {
if(jobs[i].start > finish) {
return i;
}
}
return Integer.MAX_VALUE;
}
4)k = 0で再帰を開始します。
他の部分は簡単ですが、私は再帰ルーチンのみを実装しました。コストはすべて0以上であると考えました。負のコストのジョブがある可能性がある場合は、チェックを追加して、そのジョブを考慮せずにそのまま渡す必要があります。
O(nlogn)でこれを実装できます
手順:
初期化d [0] = 0
結果は、d [n] n-間隔の数になります。
全体的な複雑さO(nlogn)
import java.util.*;
class Interval {
public int start;
public int end;
public int cost;
public Interval(int start, int end, int cost){
this.start = start;
this.end = end;
this.cost = cost;
}
}
public class BestCombinationFinder {
public int getBestCombination(List<Interval> intervals) {
if (intervals == null || intervals.size() == 0) {
return 0;
}
Collections.sort(intervals, new Comparator<Interval>() {
public int compare(Interval i1, Interval i2) {
if (i1.end < i2.end) {
return -1;
}
else if (i1.end > i2.end) {
return 1;
}
return 0;
}
});
return findBestCombination(intervals);
}
private int findBestCombination(List<Interval> intervals) {
int[] dp = new int[intervals.size() + 1];
for (int i = 1; i <= intervals.size(); i++) {
Interval currInt = intervals.get(i - 1);
int pIndex = find(intervals, currInt.start, 0, intervals.size() - 1);
dp[i] = Math.max(dp[pIndex+1] + currInt.cost, dp[i - 1]);
}
return dp[intervals.size()];
}
private int find(List<Interval> intervals, int target, int left, int right) {
if (left > right) {
return right;
}
else {
int mid = (left + right) / 2;
if (intervals.get(mid).end == target) {
return mid;
}
else if (intervals.get(mid).end > target) {
return find(intervals, target, left, mid - 1);
}
else {
return find(intervals, target, mid + 1, right);
}
}
}
}
はい、それはナップザック問題と同等です。ジョブの終了時間を考慮し、ナップザックのようにテーブルを準備します。次の解決策を読む前に、ナップザックの問題とその解決策を確認してください。
// Input:
// Jobs (stored in array jobs)
// Number of jobs (n)
find the maximum end time from given n jobs => max_end
for j from 0 to max_end do
table[0, j] := 0
end for
for i from 1 to n do
for j from 0 to max_end do
if jobs[i].end <= j then
table[i, j] := max(table[i-1, j], table[i-1, jobs[i].start] + jobs[i].cost)
else
table[i, j] := table[i-1, j]
end if
end for
end for
テーブルをたどって、スケジュールされたジョブを印刷することもできます。
j := max_end;
for i from n to 1 do
if table[i][j] != table[i-1][j]
print jobs[i]
j = jobs[i].start;
end if
end for
複雑さはナップザック問題と同じです。