200000以上の要素を含む2つの配列要素の最小積を見つける最速の方法


13

アレイがありa[n]ます。番号nは当社が入力します。私は、最小限の製品を見つける必要があるa[i]a[j]場合を:

1) abs(i - j) > k

2)a[i] * a[j]最小化されている

これが私の解決策です(非常に素朴です):

#include <iostream>
using namespace std;
#define ll long long
int main() {
    ll n,k; cin >> n >> k;

    ll a[n]; for(ll i=0;i<n;i++) cin >> a[i];

    ll mn; bool first = true;

    for(ll i=0;i<n;i++) {
        for(ll j=0;j<n;j++) {
            if(i!=j)
            if(abs(i-j) > k) {
                if(first) {
                    mn = a[i]*a[j];
                    first = false;
                } else if(a[i]*a[j] < mn) mn = a[i]*a[j];
            }
        }
    }
    cout << mn << endl;
}

しかし、距離のある最小の製品を見つけるためのより速い方法があるかどうか知りたいですか?


7
#include <bits / stdc ++。h>を使用すべきではないのはなぜですか?およびC ++は、コンパイラー拡張によって可変長配列のみを提供します。なぜ使用しないのstd::vectorですか?@Scheff-並べ替えは元の「距離」関係を破壊します。
デビッドC.ランキン

3
少なくともチェックif (i!=j) if (abs(i - j) > k)は削除できます。内側のループをi + k + 1:で開始するだけですfor (ll j = i + k + 1; j < n; ++j)。たとえばで事前に初期化firstされている場合mnは、によるチェックも削除できますmn = a[0] * a[k + 1];。(たぶん、この弾丸を作るために最初にkチェックする必要がありますn。)しかし、それはまだO(N²)です。これはより速く実行可能でなければなりません...
シェフ

2
@PaulMcKenzie インデックス距離(または最大)を持つ最小積の最初の10件のうち、2件以上の有用なヒットがあるクエリを1つ示してください。
greybeard

1
@PaulMcKenzie「この質問への回答を示すURLリンクは、数千とは言わないまでも数百とあるでしょう。」-これらのURLのうち少なくとも3つを共有してください。
גלעדברקן

2
この質問はどこから来たのですか?薄い空気で作ったもののようには聞こえません。これらの「オンライン裁判官」サイトの1つからのものであったとしても、私は驚かないでしょう。もしそうなら、それらのサイトではおそらく完全な解決策ではないにしても、問題を解決するための長い議論が繰り広げられます。
PaulMcKenzie

回答:


12

条件を満たす要素のペアが少なくとも1つあり、その中で2つの要素の乗算がオーバーフローしないと仮定すると、これは、次のようなもので、Theta(n-k)時間とTheta(1)空間の最悪および最良の場合に実行できます。

auto back_max = a[0];
auto back_min = a[0];
auto best = a[0]*a[k+1];

for(std::size_t i=1; i<n-(k+1); ++i) {
    back_max = std::max(back_max, a[i]);
    back_min = std::min(back_min, a[i]);
    best = std::min(best, std::min(a[i+k+1]*back_max, a[i+k+1]*back_min));
}

return best;

これは、時間と空間の両方の漸近的な最悪の場合の複雑さの点で最適です。最適な積は、少なくとも距離a[0]n-(k+1)要素のいずれかである可能性があるため、問題を解決するアルゴリズムk+1で少なくともn-(k+1)整数を読み取る必要があります。


アルゴリズムの背後にある考え方は次のとおりです。

最適な製品はの2つの要素を使用します。aこれらがであるa[r]と仮定しa[s]ます。一般性を失うことなくs > r、製品は可換であると仮定できます。

制限により、abs(s-r) > kこれは以下を意味しs >= k+1ます。これsで、この条件を満たす各インデックスになる可能性があるため、これらのインデックスを反復処理します。これはi示されているコードの反復ですが、k+1便宜上シフトされています(実際には問題ではありません)。各反復でi+k+1、最大のインデックスを含む最適な積を見つけ、それを以前の最良の推測と比較する必要があります。

ペアリングi+k+1する可能性のあるインデックスiは、距離の要件により、すべて小さいか等しいインデックスです。これらすべてについても繰り返す必要がありますが、固定での最小のa[i+k+1]*a[j]オーバーは製品の単調性に等しいため、不必要です(可能な2つの可能な最小と最大の両方のオーバーについて最小をとります)単調性の2つの可能な方向の兆候または同等の意味。)jimin(a[i+k+1]*max(a[j]), a[i+k+1]*min(a[j]))a[j]a[i+k+1]

a[j]ここで最適化する値のセットは{a[0], ..., a[i]}でありa[i]、の各反復で1要素()ずつ増加iするだけなので、以前の最適値よりも大きいか小さい場合に更新することで、単一の変数を追跡max(a[j])min(a[j])、単一の変数で単純に追跡できa[i]ます。これは、コード例back_maxを使用back_minして行われます。

反復の最初のステップ(i=0)はループでスキップされ、代わりに変数の初期化として実行されます。


3
@greybeard最適化製品の候補として考えa[i+k+1]られるのは最小値と最大値のみなので、それらを保持する必要はありません。
クルミ

アルゴリズムがなぜあなたの答えで機能するのか説明できますか?
MinaHany

6

最速かどうかわからない。

i <j-kのない単純な問題の場合、最小積は、2つの最小要素と最大要素のペアの積の中にあります。

したがって、(次は複雑すぎ   ます。walnut の答えを参照してください)
(•
k≤nの場合はボーク•minProductをa [0] * a [k + 1]に初期化)

  • {}と{a [ j ]で始まる2つの動的minmaxデータ構造を upToIbeyondIplusKに保持します。KJ }
  • 0からn - k -1 までの各i
    • a [ i ]をupToIに追加
    • beyondIplusKからa [ i + k ]を削除する

    • min(upToI)×min(beyondIplusK)、min(upToI)×max(beyondIplusK)、
      max(upToI)×min(beyondIplusK)およびmax(upToI)×max(beyondIplusK)の間で新しい最小積をチェックする

これは、少なくとも複雑さに関しては、最速である必要があります。O(n)時間とストレージです。
smttsp

元のソリューションの複雑度はO(N ** 2)ですが、ソリューションの複雑さをどのように推定しますか?
lenik

O(nlogn)時間、O(n)スペース(適切なminmax実装の場合)
greybeard

@老い。なぜn * logn時間が必要なのですか。なぜ単純に含まれている4 * n個の配列を保っていないminUptomaxUptominBeyondmaxBeyond(あなたは2回の繰り返しで作成することができますか)?次に、3番目の反復で、インデックスごとに、可能な最小の乗算を見つけます。
smttsp

(@smttspこれは、クルミの解法の方向への代替ステップになるでしょう。)
greybeard

4

「最小等級」の場合

2つの「最小等級」の要素を見つけ、(2つのゼロを見つけるか、配列全体を検索した後)、それらを乗算します。

abs(i - j) > kパーツなしの「最低価格」

3つの可能性があります。

  • 2つの最大(最小の大きさ)の負の数

  • 2つの最小(最小の大きさ)の負でない数

  • 最小(最大の大きさ)の負の数と最大(最大の大きさ)の非負の数

6つの値すべてを検索して、製品を見つけ出すことができます。

しかしながら; ゼロが表示されるとすぐに、最初の2つの可能性についてこれ以上知る必要がないことがわかります。そして、1つの負の数と1つの非負の数が表示されるとすぐに、3番目の可能性のみに関心があることがわかります。

これは、「3つの可能性すべてに注意」、「負の数が見られない限り答えはゼロ」、「最後の可能性のみに注意」の3つの状態を持つ有限状態マシンにつながります。これは、3つのループのセットとして実装できますgoto。(有限状態マシンの)状態が変化すると、2つのループが()別のループの中央にジャンプします。

具体的には、漠然と(テストされていない)のようになります。

   // It could be any possibility

   for(ll i=0;i<n;i++) {
       if(a[i] >= 0) {
            if(a[i] < lowestNonNegative1) {
                lowestNonNegative2 = lowestNonNegative1;
                lowestNonNegative1 = a[i];
            }
            if(lowestNonNegative2 == 0) {
                goto state2;
            }
       } else {
            if(a[i] > highestNegative1) {
                highestNegative2 = highestNegative1;
                highestNegative1= a[i];
            }
            if(lowestNonNegative1 < LONG_MAX) {
                goto state3;
            }
       }
   }
   if(lowestNonNegative2 * lowestNonNegative1 < highestNegative2 * highestNegative1) {
       cout << lowestNonNegative2 * lowestNonNegative1;
   } else {
       cout << highestNegative2 * highestNegative1;
   }
   return;

   // It will be zero, or a negative and a non-negative

   for(ll i=0;i<n;i++) {
state2:
       if(a[i] < 0) {
           goto state3;
       }
   }
   cout << "0";
   return;

   // It will be a negative and a non-negative

   for(ll i=0;i<n;i++) {
state3:
       if(a[i] < lowestNegative) {
           lowestNegative = a[i];
       } else if(a[i] > highestNonNegative) {
           highestNonNegative = a[i];
       }
    }
    cout << lowestNegative * highestNonNegative;
    return;

abs(i - j) > kパーツの「最低値」

この場合、3つの可能性があります。同じ「有限状態マシンでの3ループ」アプローチで機能させることもできますが、面倒くさく/醜くなります。この場合、より良い代替策は、アレイを事前スキャンして、ゼロがあるかどうか、およびゼロがすべて負かすべて正かを判断することです。プレスキャン後、答えがゼロであることを確認するか、特定の可能性のみを考慮して設計されたループを選択できます。


1
これはインデックスの差の下限kをどこで説明しますか?
greybeard

1
@greybeard:ありません(私はその部分を逃しました)-それを考慮に入れるためにコードを修正する必要があります。
ブレンダン

なぜ2つのゼロが必要なのですか?
TrentP

@TrentP:Argh-あなたは正しい。1つのゼロは、答えが0または負の数であることを知るのに十分です。
ブレンダン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.