自然数 A と B を含む 2 つの配列が与えられ、合計 A[i] * |B[i]-B[k]| を最小化するインデックス k を見つける必要があります。i=0 から n-1 まで。(両方の配列の長さは同じです) O(n^2) で行うのは明らかに簡単です。0 と n-1 の間のすべての k のすべての合計を計算するだけですが、実行時の複雑さを改善する必要があります。
何か案は?ありがとう!
自然数 A と B を含む 2 つの配列が与えられ、合計 A[i] * |B[i]-B[k]| を最小化するインデックス k を見つける必要があります。i=0 から n-1 まで。(両方の配列の長さは同じです) O(n^2) で行うのは明らかに簡単です。0 と n-1 の間のすべての k のすべての合計を計算するだけですが、実行時の複雑さを改善する必要があります。
何か案は?ありがとう!
これを時間O(nlogn)で行うには、最初にBの値に基づいて両方の配列を並べ替えてから、1回のスキャンを実行します。
配列が並べ替えられたら、i>kの場合はB[i]> = B [k]、i<=kの場合はB[i]<= B [k]なので、合計は次のように書き換えることができます。
sum A[i] * abs(B[i]-B[k]) = sum A[i]*(B[i]-B[k]) for i=k..n-1
+ sum A[i]*(B[k]-B[i]) for i=0..k-1
= sum A[i]*B[i] for i=k..n-1
- B[k] * sum A[i] for i=k..n-1
+ B[k] * sum A[i] for i = 0..k-1
- sum A[i]*B[i] for i = 0..k-1
時間O(n)のすべての合計を事前計算できます。これにより、O(n)のすべての位置でターゲットの合計を評価し、最高のスコアが得られるkの値を選択できます。
これができるのはO(n log n)だと思います。
まず、配列を並べ替えB
、同じ順列を配列に適用しA
ます(そして順列を記憶します)。これはO(n log n)の部分です。すべてのiを合計するため、A配列とB配列に同じ順列を適用しても、最小値は変更されません。
ソートされたB
配列では、アルゴリズムの残りの部分は実際にはO(n)です。
kごとに、配列C k [i] = | B [i] --B[k]|を定義します。
(注:実際にはC kを構築しません...推論を容易にするための概念として使用します。)
最小化しようとしている量( k以上)がA [i] * Ck[i]の合計であることに注意してください。先に進んで、それに名前を付けましょう:
定義: Sk= ΣA [i]*C k [i]
さて、特定のkについて、C kはどのように見えますか?
ええと、明らかにC k [k]=0です。
さらに興味深いことに、B配列はソートされているため、絶対値の符号を取り除くことができます。
さらに2つのことを定義しましょう。
定義: Tk =ΣA[i]for0 <= i < k
定義: Uk= ΣA [i]fork <i <n
(つまり、T kはAの最初のk-1要素の合計です。UkはAの最初のk要素を除くすべての要素の合計です。)
重要な観察:S k、T k、およびU kが与えられると、一定時間でS k + 1、T k + 1、およびU k+1を計算できます。どのように?
TとUは簡単です。
問題は、どのようにしてSkからSk + 1に到達するかということです。
C k +1に行くとCkがどうなるか考えてみましょう。0からkまでのCのすべての要素にB[k+ 1] -B [k]を加算し、k + 1からnまでのCのすべての要素から同じ量を減算します(これを証明します)。つまり、SkからSk +を取得するには、T k *(B [k + 1]-B [k])を加算し、U k *(B [k + 1] -B [k])を減算するだけです。 1。
代数的に...Skの最初のk項は、 A [i] *(B [k]-B [i])の0からk-1までの合計です。
S k + 1の最初のk項は、A [i] *(B [k + 1]-B [i])の0からk-1までの合計です。
これらの違いは、(A [i] *(B [k + 1] --B [i])-(A [i] *(B [k] --B [ i]))。A [i]項を因数分解し、B [i]項をキャンセルして、A [i] *(B [k + 1] --B [k])の0からk-1までの合計を取得します。 、これはT k *(B [k + 1]-B [k])です。
同様に、 Skの最後のnk-1項についても同様です。
S 0、T 0、およびU 0を線形時間で計算でき、SkからSk + 1まで一定時間で移動できるため、すべてのSkを線形時間で計算できます。それで、それをして、最小のものを覚えてください、そしてあなたは終わりです。
ソート順列の逆を使用してk
、元の配列のを取得します。
これが O(NlogN) ソリューションです。例
A 6 2 5 10 3 8 7
B 1 5 4 3 6 9 7
1) 最初に 2 つの配列を B の昇順で並べ替えます。A の要素は B とバインドされているだけです。並べ替え後、次のようになります。
A 6 10 5 2 3 7
B 1 3 4 5 6 7
Bは今順番にあるので。我々は持っています
n-1
sum A[i]|B[i]-B[k]|
i=0
k-1 n-1
=sum A[i](B[k]-B[i])+ sum A[i](B[k]-B[i])
i=0 i=k+1
k-1 n-1 k-1 n-1
=B[k](sum A[i] -sum A[i]) - (sum A[i]B[i]- sum A[i]B[i])
i=0 i=k+1 i=0 i=k+1
2) 配列 A のプレフィックスサムを計算します sumA=0 6 16 21 23 26 33
i=e
With sumA sum A[i] can be calcuated in O(1) time for any s and e.
i=s
同じ理由で、A[i]B[i] のプレフィックスサムを計算できます。したがって、各 k の値を確認するには、O(1) 時間かかります。したがって、総時間の複雑さはO(NlogN)+O(N).