6

保存された(適切と思われる)数列に対して次の操作を効率的にサポートできるデータ構造を設計する必要があります。

  • xシーケンスの最初のi要素に整数を追加します
  • kシーケンスの最後に整数を追加します
  • シーケンスの最後の要素を削除します
  • シーケンス内のすべての要素の平均を取得します

空のシーケンスから開始[]

  • 0を追加([0]
  • 追加5([0, 5]
  • 付録6([0, 5, 6]
  • シーケンスの最初の2つの要素に3を追加します([3, 8, 6]
  • 平均5.66を取得します([3, 8, 6]
  • 最後の要素を削除します([3, 8]
  • 平均5.5を取得する([3, 8]

前作

Fenwick TreesTopcoder Editorial )を使用することを考えましたが、そのためには、必ずしもわからないFenwickTreeの初期化のためのシーケンスの最大サイズを指定する必要があります。O(lg N)ただし、シーケンスでサポートできる要素の最大数がある場合は、シーケンス内のすべての要素の合計も保存すれば、それらの操作をサポートできます。

編集:質問はCodeforcesの問題であり、すべての操作に劣線形の実行時間が必要です。最初の要素に追加することは、最悪の場合、シーケンス全体に追加することと同じになる可能性があるためです。

4

6 に答える 6

6

リンクリストに現在の長さと合計を加えたものを使用することを検討しましたか?各操作について、一定の追加作業で現在の平均を維持できます(リストの長さと合計がわかっていて、すべての操作でこれら2つの値が一定の方法で変更されます)。

唯一の非定数操作は、任意のプレフィックスに定数を追加することです。これは、各数値を調整する必要があるため、プレフィックスのサイズに比例して時間がかかります。

すべての操作を一定(償却)にするには、より多くの作業が必要です。二重リンクリストを使用する代わりに、スタックで配列をバックアップします。i配列の各スロットには、での数値iと、までのすべての要素に追加される定数の両方が含まれるようになりましたi。(「要素11までのすべての要素に3を追加する」と言うと、スロット11には番号3が含まれますが、スロット0〜10は空になります。)これで、新しい要素を追加することを除いて、すべての操作が以前と同じになります。標準の配列倍増トリックが含まれ、キューの最後から最後の要素をポップするときは、(a)そのスロットに定数を追加し、(b)スロットからスロットの定数に定数値を追加する必要がありiますi-1。だからあなたの例のために:

追加0: [(0,0)], sum 0, length 1

付録5: ([(0,0),(5,0)], sum 5, length 2

付録6: [(0,0),(5,0),(6,0)], sum 11, length 3

シーケンスの最初の2つの要素に3を追加します。[(0,0),(5,3),(6,0)], sum 17, length 3

平均5.66を取得します

最後の要素を削除します[(0,0),(5,3)], sum 11, length 2

平均5.5を取得します

最後の要素を削除します[(0,3)], sum 3, length 1

アイデアをおそらくより明確に示すJavaコードを次に示します。

class Averager {
  private int sum;
  private ArrayList<Integer> elements = new ArrayList<Integer>();
  private ArrayList<Integer> addedConstants = new ArrayList<Integer>();

  public void addElement(int i) {
    elements.add(i);
    addedConstants.add(0);
    sum += i;
  }

  public void addToPrefix(int k, int upto) {
    addedConstants.set(upto, addedConstants.get(upto) + k);
    sum += k * (upto + 1);
    // Note: assumes prefix exists; in real code handle an error
  }

  public int pop() {
    int lastIndex = addedConstants.length() - 1;

    int constantToAdd = addedConstants.get(lastIndex);
    int valueToReturn = elements.get(lastIndex);
    addedConstants.set(
      lastIndex-1,
      addedConstants.get(lastIndex-1) + constantToAdd);
    sum -= valueToReturn;
    elements.remove(lastIndex);
    addedConstants.remove(lastIndex);
    return valueToReturn + constantToAdd;
    // Again you need to handle errors here as well, particularly where the stack
    // is already empty or has exactly one element
  }

  public double average() {
    return ((double) sum) / elements.length();
  }
}
于 2013-03-19T21:14:29.280 に答える
2

現在の合計とカウントだけでなく、ヘッドとテールの参照を維持する二重リンクリストのように聞こえます。

シーケンスの最初のi要素に整数xを追加します

* headから始めてx、次の項目を追加します。を繰り返しiます。sum += i*x

シーケンスの最後に整数kを追加します

* tailから始めて、head = tail、tail=nullで新しいアイテムを作成します。それに応じて*tail、sum、countを更新します。

シーケンスの最後の要素を削除します

*tailを*tail->prevに更新します。合計を更新し、カウントをデクリメントします

平均5.5を取得します([3、8])

合計/カウントを返す

于 2013-03-19T21:17:40.063 に答える
1

バイナリインデックスツリーを使用してみることをお勧めします。

O(Log(n))の累積度数にアクセスできます。

log(i)の順序で最初のi要素に追加することもできます。

ただし、最初のi要素をXだけ増やす代わりに、n番目の要素をXだけ増やします。

最後の要素を削除するには、おそらく、累積的に削除された量を合計する別のツリーがあります。(したがって、削除する代わりに、最初のツリーにアクセスするときに常に結果から減算する別のツリーにその量を追加します)。

追加するために、私はあなたがあなたに部屋を与えるであろうサイズ2*Nの木から始めることを提案します。次に、2 * Nより大きくなる場合は、サイズ2*Nの別のツリーを追加します。(これを行うための最良の方法については正確にはわかりませんが、うまくいけばそれを理解することができます)。

于 2013-03-19T21:45:10.047 に答える
1

このデータ構造は、タプル(N、S)にすることができます。ここで、Nはカウントであり、Sは数値の合計とスタックです。特別なことは何もありません。最初のO(i)を除いて、すべての操作はO(1)です。

于 2013-03-19T21:16:52.833 に答える
1

最初の要件を満たすために、追加操作の個別のデータ構造を維持できます。基本的に、これは範囲と増分の順序付けられたコレクションです。また、これらの追加の合計を維持します。したがって、最初の3つのアイテムに5を追加し、次に最初の10のアイテムに12を追加すると、次のようになります。

{3, 5}
{10, 12}

そして、それらの加算の合計は(3*5) + (10*12)=135です。

合計を求められたら、アイテムの合計とこれらの追加の合計を提供します。

あなたが持っている唯一の問題は、リストの最後のアイテムを削除するときです。次に、この追加のコレクションを調べて、最後のアイテム(削除するアイテム)を含むものを見つける必要があります。そのデータ構造は、キーがインデックスであるハッシュマップである可能性があります。したがって、上記の例では、ハッシュマップは次のようになります。

key: 3  value: 5
key: 10 value: 12

その最初の操作を行うときはいつでも、ハッシュマップをチェックして、そのキーを持つアイテムがすでに存在するかどうかを確認します。その場合、新しい増分を追加するのではなく、そこで値を更新するだけです。それに応じて合計を更新します。

面白い。追加の合計を保持する必要はありません。あなたがそれにいる間、あなたは合計を更新することができます。

リストから最後のアイテムを削除するときは、そのキーを持つアイテムのハッシュマップを確認します。存在する場合は、そのアイテムを削除し、キーを減らしてから、ハッシュマップに追加し直します(または、既存のアイテムが存在する場合は、そのキーで更新します)。

したがって、mattedgodによって提案された二重リンクリストを、彼が提案した合計とともに使用します。次に、このハッシュマップを使用して、リストへの追加のコレクションを維持し、それに応じて合計を更新します。

于 2013-03-19T22:18:06.957 に答える
0

ラウンド#174の問題設定者は、このラウンドの社説を公開しました。ここで見つけることができます。また、いくつかの受け入れられているソリューションを見ることができます:PythonC++

于 2013-03-19T21:45:03.253 に答える