4

一連an番号が与えられます。シーケンスの削減は、要素とをa置き換えることとして定義されます。a[i]a[i+1]max(a[i],a[i+1])

各削減操作には、として定義されたコストがありますmax(a[i],a[i+1])。縮小後n-1、長さのシーケンス1が取得されます。

aここでの目標は、結果として得られる長さ1のシーケンスのコストが最小になるように、指定されたシーケンスの最適な削減のコストを出力することです。

例えば:

1

2

3

Output :

5

O(N ^ 2)ソリューションは簡単です。何か案は?

編集1:人々は私のアイデアについて尋ねているので、私のアイデアはペアごとにシーケンスをトラバースし、各ペアのコストをチェックし、最終的に最小のコストでペアを減らすことでした。

1 2 3
 2 3     <=== Cost is 2

したがって、上記のシーケンスを次のように減らします

2 3

ここで再びシーケンスをトラバースすると、コストは3になります。

2 3
 3       <=== Cost is 3

したがって、総コストは2 + 3=5です。

上記のアルゴリズムはO(N ^ 2)のものです。そのため、私はもっと最適化されたアイデアを求めていました。

4

4 に答える 4

2

O(n)解決:

高いレベル:

基本的な考え方は、隣接する要素と最小の隣接要素eの両方よりも小さい要素を繰り返しマージすることです。マージのコストと結果の両方がであるため、これにより最小のコストが生成されます。つまり、マージによって要素を現在よりも小さくすることはできません。したがって、可能な限り最も安価なマージはであり、そのマージによって他のコストを増やすことはできません。可能なマージ。nsnlnsmax(a[i],a[i+1])ens

これは、配列の要素のスタックを降順で保持することにより、ワンパスアルゴリズムで実行できます。現在の要素を両方の隣接要素(1つはスタックの最上位)と比較し、完了するまで適切なマージを実行します。

擬似コード:

stack = empty
for pos = 0 to length
  // stack.top > arr[pos] is implicitly true because of the previous iteration of the loop
  if stack.top > arr[pos] > arr[pos+1]
    stack.push(arr[pos])
  else if stack.top > arr[pos+1] > arr[pos]
    merge(arr[pos], arr[pos+1])
  else while arr[pos+1] > stack.top > arr[pos]
    merge(arr[pos], stack.pop)

Javaコード:

Stack<Integer> stack = new Stack<Integer>();
int cost = 0;
int arr[] = {10,1,2,3,4,5};
for (int pos = 0; pos < arr.length; pos++)
  if (pos < arr.length-1 && (stack.empty() || stack.peek() >= arr[pos+1]))
    if (arr[pos] > arr[pos+1])
      stack.push(arr[pos]);
    else
      cost += arr[pos+1]; // merge pos and pos+1
  else
  {
    int last = Integer.MAX_VALUE; // required otherwise a merge may be missed
    while (!stack.empty() && (pos == arr.length-1 || stack.peek() < arr[pos+1]))
    {
      last = stack.peek();
      cost += stack.pop(); // merge stack.pop() and pos or the last popped item
    }
    if (last != Integer.MAX_VALUE)
    {
      int costTemp = Integer.MAX_VALUE;
      if (!stack.empty())
        costTemp = stack.peek();
      if (pos != arr.length-1)
        costTemp = Math.min(arr[pos+1], costTemp);
      cost += costTemp;
    }
  }
System.out.println(cost);
于 2013-03-05T09:05:09.097 に答える
0

リストを並べ替えるのが不正だと思わない場合は、n log n時間内に並べ替えてから、最初の2つのエントリを再帰的にマージします。この場合の合計コストは、エントリの合計から最小のエントリを引いたものになります。これは最適です

  1. n-1費用はエントリーの合計になります(繰り返しが許可されています)
  2. i最小のエントリは、ほとんどの場合、コスト関数に表示されi-1ます

リストがソートされていなくても、同じ基本的な考え方が機能します。最適な解決策は、最小の要素を最小の要素とマージすることです。これが最適であることを確認するには、次の点に注意してください。

  1. n-1費用はエントリーの合計になります(繰り返しが許可されています)
  2. エントリa_iは、ほとんどj-1の場合、コスト関数に表示されます。ここで、は、サブシーケンスの最大要素であるものをj含む、最も長い連続したサブシーケンスの長さです。a_ia_i

最悪の場合、シーケンスは減少し、時間はO(n^2)です。

于 2013-03-05T05:38:19.427 に答える
0

max(a[i],a[i+1])削減の「コスト」「計算コスト」、つまり時間のかかる操作や、単純に計算したいものを意味するのか、私は混乱しています。後者の場合、次のアルゴリズムはO(n ^ 2)よりも優れています。

  1. リストをソートします。より正確には、 b[i]sta[b[i]]はソートされたリストです。RADIXソートを使用できる場合はO(n)、それ以外の場合はO(n log n)です。
  2. ソートされたリストの2番目に低い項目から開始しiます。左/右がiよりも小さい場合は、削減を実行します。各項目に対してO(1)、合計2からリストを更新します。

それが最適な解決策であるかどうかはわかりませんが、整数の場合はO(n)、そうでない場合はO(n log n)です。

編集:事前計算ステップを削除すると、はるかに簡単になることに気づきました

于 2013-03-05T02:05:58.820 に答える
0

貪欲なアプローチは確かに機能します。

あなたはいつでもその小さい隣人で最小の数を減らすことができます。

証明:ある時点で最小数を減らす必要があります。ネイバーを減らすと、ネイバーの値が少なくとも同じ(おそらく)大きくなるため、最小要素a[i]を減らす操作のコストは常にc>= min(a [i-1]、a [i + 1])

今、私たちはする必要があります

  1. 最小数をすばやく見つけて削除
  2. その隣人を見つける

その上で2つのRMQを使用します。バイナリ検索として操作2を実行します。これにより、O(N * log ^ 2(N))が得られます

編集:最初のRMQ-値。要素を削除するときは、
2番目のRMQである「プレゼンス」に大きな値を入れます。0または1(値はある/ない)。l[たとえば]a[i]の左隣を見つけるには、最大の、それを見つける必要がありますsum[l,i-1] = 1

于 2013-03-05T02:58:34.607 に答える