1

N 個のジョブが与えられ、すべてのジョブは次の 3 つの要素で表されます。

1) 開始時間

2) 終了時間。

3) 関連する利益または価値。

サブセット内の 2 つのジョブが重ならないように、ジョブの最大利益サブセットを見つけます。

O(N ^ 2)の複雑さを持つ動的計画法ソリューションを知っています(現在の間隔をマージし、マージがi番目まで最大になる間隔を取る前の要素をチェックする必要があるLISに近い)このソリューションは、バイナリ検索と単純な並べ替えを使用して O(N*log N ) にさらに改善できます。

しかし、私の友人は、セグメント ツリーと二分探索を使用することによっても解決できると教えてくれました。セグメント ツリーをどこで、どのように使用するかについての手がかりがありません。

手伝ってくれますか?

リクエストに応じて、申し訳ありませんがコメントされていません

私がやっていることは、開始インデックスに基づいてソートし、前の間隔とそれらの最大取得可能値をマージして、DP[i] に i まで取得可能な最大値を格納することです!

 void solve()
    {
        int n,i,j,k,high;
        scanf("%d",&n);
        pair < pair < int ,int>, int > arr[n+1];// first pair represents l,r and int alone shows cost
        int dp[n+1]; 
        memset(dp,0,sizeof(dp));
        for(i=0;i<n;i++)
            scanf("%d%d%d",&arr[i].first.first,&arr[i].first.second,&arr[i].second);
        std::sort(arr,arr+n); // by default sorting on the basis of starting index
        for(i=0;i<n;i++)
        {
            high=arr[i].second;
            for(j=0;j<i;j++)//checking all previous mergable intervals //Note we will use DP[] of the mergable interval due to optimal substructure
            {
                if(arr[i].first.first>=arr[j].first.second)  
                        high=std::max(high , dp[j]+arr[i].second);
            }
            dp[i]=high;
        }
        for(i=0;i<n;i++)
                dp[n-1]=std::max(dp[n-1],dp[i]);
        printf("%d\n",dp[n-1]);
    }

int main()
{solve();return 0;}

編集: 私の作業コードは、最終的にデバッグするのに3時間かかりました! さらに、このコードは、定数が大きく、実装が悪いため、バイナリ検索およびソートよりも遅くなります:P (参照用)

#include<stdio.h>
#include<algorithm>
#include<vector>
#include<cstring>
#include<iostream>
#include<climits>
#define lc(idx) (2*idx+1)
#define rc(idx) (2*idx+2)
#define mid(l,r) ((l+r)/2)
using namespace std;
int Tree[4*2*10000-1];
void update(int L,int R,int qe,int idx,int value)
{

    if(value>Tree[0])
        Tree[0]=value;
    while(L<R)
    {
        if(qe<= mid(L,R))
        {
            idx=lc(idx);
            R=mid(L,R);
        }
        else
        {
            idx=rc(idx);
            L=mid(L,R)+1;
        }
        if(value>Tree[idx])
            Tree[idx]=value;

    }
    return ;
}
int Get(int L,int R,int idx,int q)
{
    if(q<L )
        return 0;
    if(R<=q)
        return Tree[idx];

    return max(Get(L,mid(L,R),lc(idx),q),Get(mid(L,R)+1,R,rc(idx),q));

}
bool cmp(pair < pair < int , int > , int > A,pair < pair < int , int > , int > B)
{
    return A.first.second< B.first.second;
}
int main()
{

        int N,i;
        scanf("%d",&N);
        pair < pair < int , int  > , int > P[N];
        vector < int > V;
        for(i=0;i<N;i++)
        {
            scanf("%d%d%d",&P[i].first.first,&P[i].first.second,&P[i].second);
            V.push_back(P[i].first.first);
            V.push_back(P[i].first.second);
        }
        sort(V.begin(),V.end());
        for(i=0;i<N;i++)
        {
            int &l=P[i].first.first,&r=P[i].first.second;
            l=lower_bound(V.begin(),V.end(),l)-V.begin();
            r=lower_bound(V.begin(),V.end(),r)-V.begin();
        }
        sort(P,P+N,cmp);
        int ans=0;
        memset(Tree,0,sizeof(Tree));
        for(i=0;i<N;i++)
        {
            int aux=Get(0,2*N-1,0,P[i].first.first)+P[i].second;
            if(aux>ans)
                ans=aux;
            update(0,2*N-1,P[i].first.second,0,ans);
        }
        printf("%d\n",ans);

    return 0;
}
4

1 に答える 1

5
high=arr[i].second;
for(j=0;j<i;j++)//checking all previous mergable intervals //Note we will use DP[] of the mergable interval due to optimal substructure
{
    if(arr[i].first.first>=arr[j].first.second)  
    high=std::max(high, dp[j]+arr[i].second);
}
dp[i]=high;

O(log n)これは、セグメント ツリーで実行できます。

まず、少し書き直してみましょう。iと の両方を含む合計の最大値を取るため、取得している最大値は少し複雑jです。でもiこの部分は一定なので抜きましょう。

high=dp[0];
for(j=1;j<i;j++)//checking all previous mergable intervals //Note we will use DP[] of the mergable interval due to optimal substructure
{
    if(arr[i].first.first>=arr[j].first.second)  
    high=std::max(high, dp[j]);
}
dp[i]=high + arr[i].second;

[0, i - 1]これで、条件を満足する値の中から最大値を決定する問題が軽減されましたif

がなければif、セグメント ツリーの単純なアプリケーションになります。

今、2つの選択肢があります。

1.セグメント ツリーのO(log V)クエリ時間とメモリを処理するO(V)

V間隔のエンドポイントの最大サイズはどこにありますか。

を移動するときに間隔の開始点を挿入するセグメント ツリーを作成できますi。次に、値の範囲に対してクエリを実行します。このようなもので、セグメント ツリーは-infinitysize に初期化されますO(V)

Update(node, index, value):
  if node.associated_interval == [index, index]:
    node.max = value
    return

  if index in node.left.associated_interval:
    Update(node.left, index, value)
  else:
    Update(node.right, index, value)

  node.max = max(node.left.max, node.right.max)

Query(node, left, right):
  if [left, right] does not intersect node.associated_interval:
    return -infinity

  if node.associated_interval included in [left, right]:
    return node.max

  return max(Query(node.left, left, right),
             Query(node.right, left, right))

[...]

high=Query(tree, 0, arr[i].first.first)
dp[i]=high + arr[i].second;
Update(tree, arr[i].first.first, dp[i])

O(log n)2. セグメント ツリーのクエリ時間とO(n)メモリの削減

間隔の数はそれらの長さよりも大幅に少ない可能性があるため、それらの長さもO(n). 確かに、できます。

これには、範囲内で間隔を正規化することが含まれます[1, 2*n]。次の間隔を考慮してください

8 100
3 50
90 92

それらを直線上にプロットしてみましょう。それらは次のようになります。

3 8 50 90 92 100

次に、それぞれをインデックスに置き換えます。

1 2 3  4  5  6
3 8 50 90 92 100

そして、あなたの新しい間隔を書いてください:

2 6
1 3
4 5

それらは最初の間隔のプロパティを保持することに注意してください。同じ間隔が重複している、同じ間隔が互いに含まれているなどです。

これはソートで実行できます。size のセグメント ツリーを宣言することを除いて、同じセグメント ツリー アルゴリズムを適用できるようになりました2*n

于 2015-07-12T20:02:34.227 に答える