Google Code Jam万里の長城問題のより速い解決策はありますか


16

次のGoogle Code Jamラウンド1Cの質問を検討してください。

万里の長城は無限の線から始まり、すべての場所の高さはです。0

いくつかの部族NN1000は、開始日D、開始強度S、開始西座標W、開始東座標Eのパラメーターに従って、壁を壁に攻撃します。E。この最初の攻撃は、D日、範囲[W,E]、強度Sます。[W、E]内に[W,E]高さ<Sの万里の長城の部分がある場合<S、攻撃は成功し、その日の終わりに、[W,E]の高さ<Sは高さになるS(またはその日、他の攻撃が同じセグメントに強度ヒットした場合)S>S

各部族は退却する前に最大攻撃を実行し、各攻撃はその前の攻撃から繰り返し決定されます。すべての部族は、いくつか持っている、、および攻撃の彼らの順序を決定します。お待ちしております攻撃の間に日を、彼らは攻撃範囲に移動します各攻撃(ネガティブ=西、正の単位を=東)、ただし、範囲のサイズは同じままであり、各攻撃の後、その強さも一定の値で増加/減少します。1000δDδXδSδD1δX

問題の目標は、攻撃している部族の完全な説明が与えられれば、攻撃が成功する数を決定することです。

約20秒で動作するソリューションをコーディングできました。実装したソリューションにはO(AlogA+(A+X)logX)時間かかります。ここで、A=攻撃の総数シミュレーション(最大1000000)、およびX=攻撃範囲の一意のエッジポイントの総数(最大2000000)。

高いレベルで、私のソリューション:

  • すべての部族情報を読み込みます
  • 攻撃範囲のすべての一意のX座標を計算しますO(A)
  • 最小の高さの値を追跡するX範囲上の遅延更新されたバイナリツリーとして壁を表します。リーフは、間に何もない2つのX座標のスパンであり、すべての親ノードは、子によってカバーされる連続的な間隔を表します。- O(XlogX)
  • すべての部族が実行するすべての攻撃を生成し、日ごとにソートしますO(AlogA)
  • 各攻撃について、成功するかどうかを確認します(logXクエリ時間)。日が変わったら、未処理のすべての成功した攻撃をループし、それに応じてウォールを更新しますlogX各攻撃の\ log X更新時間)。- O(AlogX)

私の質問はこれです:よりも良い方法はありか?おそらく、部族の連続攻撃の線形性を利用する戦略的な方法はありますか?20秒は、意図したソリューションには長すぎると感じます(ただし、Javaはそのせいかもしれません)。O(AlogA+(A+X)logX)


3
閉じないでください。有効な質問です。答えは下限証明であり、それが実際に私たちができる最善の方法である場合、私たちはより良くできないことを示します。例えば、私はここで要素の明確性の問題を使用できるかもしれないと推測していますが、それについて考える時間を見つけていません。
アリヤバータ

私はそれを開いたままにしておきます:)
トルクストーム

回答:


2

明らかな改善の余地は次のステップです。

すべての部族が実行するすべての攻撃を生成し、日ごとにソートしますO(AlogA)

部族は特定の日から一定の間隔で攻撃することを知っています。つまり、多くの事前ソート済みリストを本質的にマージする必要があるということです。また、問題文は、1,000を超える部族(マージする1,000のリスト)が決して存在しないことを示しています。1,000,000回の最大攻撃と比較してごくわずかです!実装の相対的なタイミングによっては、これを切り替えると処理時間が半分に短縮される可能性があります。

理論上の複雑さを最適化するために提案できるのはこれだけです。しかし、この変更後に最適になるという証拠はありません。


私はパズルを自分で試してみましたが、壁のかなり暗い表現を使用しましたstd::map。壁の高さが変化する場所を格納するバイナリ検索ツリー(正確にはC ++の)です。これにより、必要に応じてノードを追加および削除できました(つまり、複雑なセクションが大規模な圧倒的な攻撃、または同じ強度の複数の攻撃にさらされた場合、ノードの数は大幅に減少します)。これにより、3.9秒で大規模な入力が解決されました(私の中間仕様の開発用ラップトップ)。改善にはいくつかの理由があると思います。

  • あなたが指摘したように、ボクシングとアンボクシングは高価になる可能性がありますが、C ++のテンプレートベースのコンテナはこれを完全に回避します。
  • 私が使用した壁の表現は理論的に悪いですが、ほとんどの場合、ノードの数を動的に減らすことができるため、非常に高速になりました(ほとんどのテストケースは1kノード未満で最大化され、2を除くすべてが10k未満でした) 。実際、かなりの時間がかかった唯一のケースは#7で、これは交差しない範囲の多くをテストしていたようです。
  • 私は前処理を使用しませんでした(ステージは、各部族が次に攻撃するタイミングを追跡し、各ターンでジョイントの最低値を検索することによって決定されます)。繰り返しますが、これは理論的には悪いですが、ほとんどの場合、オーバーヘッドが低いことはそれがより速いことを疑っています(これをテストして戻ってきます)。 更新:上記の方法と同様に、攻撃用の優先度キューを追加しました(ただし、大規模な配列を作成する代わりにオンザフライで計算しました)。

要するに、私はあなたのアルゴリズムは一般的なケースでは最適に近いと思いますが、典型的な入力に対してそれをスピードアップできるいくつかの方法があります。


1

以下は回答であるため、質問から削除されました。

他の議論と成功した解決策を見ると、私が説明した解決策はほとんど予想されるアルゴリズムであることを示しているようです。私のソリューションのスローダウンは、おそらく配列ベースのものではなく、自動ボックス化とポインタベースのツリー構造の怠慢な使用が原因である可能性があります-したがって、ソリューションが存在する場合、おそらく全体ではないと思われますここにあるものよりもずっと良い。

解決策はこちらにあります。これは私がここに投稿したものとほとんど同じです。だから私はもっと効率的な解決策は存在しないと信じたいと思っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.