web-dev-qa-db-ja.com

区間木における重複しない最大間隔

時間間隔のリストが与えられた場合、重複しない最大間隔のセットを見つける必要があります。

例えば、

次の間隔がある場合:

[0600, 0830], [0800, 0900], [0900, 1100], [0900, 1130], 
[1030, 1400], [1230, 1400]

また、時間は[0000, 2400]の範囲内でなければならないことが与えられています。

重複しない間隔の最大セットは[0600, 0830], [0900, 1130], [1230, 1400]です。

最大セットパッキングがNP完全であることを理解しています。問題(開始時間と終了時間のみを含む間隔)もNP完全であるかどうかを確認したいと思います。

もしそうなら、指数関数的な時間で最適なソリューションを見つける方法がありますが、よりスマートな前処理とデータのプルーニングを使用します。または、固定パラメータの扱いやすいアルゴリズムを実装するのが比較的簡単な場合。近似アルゴリズムには行きたくありません。

15
shobhu

これはNP完全問題ではありません。この問題を解決するために動的計画法を使用するO(n * log(n))アルゴリズムを考えることができます。

N個の間隔があるとします。与えられた範囲がS(あなたの場合は_S = [0000, 2400]_)であると仮定します。すべての間隔がS内にあると仮定するか、線形時間でS内にないすべての間隔を削除します。

  1. 前処理:

    • すべての間隔を開始点で並べ替えます。 n間隔の配列_A[n]_を取得するとします。
      • このステップにはO(n * log(n))時間がかかります
    • 区間のすべての終点について、その後に続く最小の始点のインデックスを見つけます。 n整数の配列_Next[n]_を取得するとします。
      • そのような開始点が間隔_i,_の終了点に存在しない場合、nを_Next[i]_に割り当てることができます。
      • これは、すべての区間のn個のエンドポイントを列挙することでO(n * log(n))時間内に実行でき、バイナリ検索を使用して答えを見つけます。これを解決するための線形アプローチが存在するかもしれませんが、前のステップではすでにO(n * log(n))時間がかかるため、問題ではありません。
  2. DP:

    • 範囲_[A[i].begin, S.end]_の重複しない最大間隔が_f[i]_であると仮定します。次に、_f[0]_が必要な答えです。
    • また、_f[n] = 0_;
    • 状態遷移方程式:
      • _f[i] = max{f[i+1], 1 + f[Next[i]]}_
    • DPステップに線形時間がかかることは明らかです。

上記の解決策は、私が問題を一目見たときに思いついたものです。その後、私はより単純な貪欲なアプローチも考えます(ただし、大きなO表記の意味では速くはありません)。

(上記のDPアプローチと同じ表記法と仮定で)

  1. 前処理:すべての間隔をendポイントで並べ替えます。 n間隔の配列_B[n]_を取得するとします。

  2. 貪欲:

    _int ans = 0, cursor = S.begin;
    for(int i = 0; i < n; i++){
        if(B[i].begin >= cursor){
            ans++;
            cursor = B[i].end;
        }
    }
    _

上記の2つの解決策は私の頭から浮かびますが、あなたの問題はアクティビティ選択問題とも呼ばれ、ウィキペディアで見つけることができます http://en.wikipedia.org/wiki/Activity_selection_problem

また、アルゴリズムの概要では、この問題について16.1で詳しく説明しています。

25
jeffrey