200000+ eleman içeren 2 dizi elemandan en az ürünü bulmanın en hızlı yolu


13

Bir dizim var a[n]. Numara ntarafımızdan girilir. Ben minimal ürünü bulmak gerekir a[i]ve a[j]eğer:

1) abs(i - j) > k

2) a[i] * a[j]küçültülmüş

İşte benim çözümüm (çok naif):

#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;
}

Ama mesafeli minimum bir ürün bulmanın daha hızlı bir yolu olup olmadığını bilmek istiyorum?


7
Neden #inc <<bits / stdc ++. H> yazmamalıyım? ve C ++ yalnızca derleyici uzantısıyla Değişken Uzunluk Dizisi sağlar . Neden kullanmıyorsun std::vector? @Scheff - sıralama orijinal "mesafe" ilişkilerini yok eder.
David C. Rankin

3
En azından kontrol if (i!=j) if (abs(i - j) > k)ortadan kaldırılabilir. Sadece i de iç döngü başlatmak + k + 1: for (ll j = i + k + 1; j < n; ++j). Önce ile başlatılırsa, ile kontrol firstde ortadan kaldırılabilir . (Belki, bu kurşun geçirmez hale getirmek için başlangıçta kontrol edilmelidir .) Ama yine de O (N²). Bu daha hızlı yapılabilir olmalı ...mnmn = a[0] * a[k + 1];kn
Scheff

2
@PaulMcKenzie Lütfen dizin mesafesi (veya maksimal) olan minimum ürün için ilk on içinde en az iki yararlı isabet içeren bir sorgu gösterin .
greybeard

1
@PaulMcKenzie "Bu sorunun cevabını gösteren binlerce URL bağlantısı olmasa bile yüzlerce olabilir." - lütfen bu URL'lerden en az üçünü paylaşın.
ברקן ברקן

2
Bu soru nereden geldi? İnce havadan yapılmış bir şey gibi gelmiyor. Bu "çevrimiçi yargıç" sitelerinden biri olması sürpriz olmaz. Eğer öyleyse, bu siteler muhtemelen tam çözümler olmasa bile sorunun çözülmesi üzerine uzun süren tartışmalar düzenliyor.
PaulMcKenzie

Yanıtlar:


12

Koşulları karşılayan en az bir çift eleman olduğu ve içindeki iki öğenin çoğalmasının olmadığı varsayıldığında, bu, Theta(n-k)zaman ve Theta(1)mekanda en kötü ve en iyi durumda, böyle bir şeyle yapılabilir:

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;

Bu, hem zaman hem de alan için asimptotik en kötü durum karmaşıklığı açısından en uygunudur, çünkü optimal ürün en azından uzaktaki elemanlardan a[0]herhangi biri ile olabilir , bu nedenle en azından tamsayılar sorunu çözen herhangi bir algoritma tarafından okunmalıdır.n-(k+1)k+1n-(k+1)


Algoritmanın arkasındaki fikir şöyledir:

Optimal ürün iki element kullanır a, bunların a[r]ve varsayalım a[s]. Genelliği kaybetmeden,s > r , ürünün değişmeli olduğu .

Kısıtlama nedeniyle abs(s-r) > kbu ima eder s >= k+1. Şimdi sbu koşulu karşılayan endekslerin her biri olabilir, bu yüzden bu endeksleri tekrarlıyoruz. iGösterilen koddaki yineleme budur , ancak k+1kolaylık sağlamak için değiştirilir (gerçekten önemli değildir). Her bir yineleme için en i+k+1büyük endeks olarak en uygun ürünü bulmamız ve bir önceki en iyi tahminle karşılaştırmamız gerekir.

Eşleştirilebilecek olası endekslerin i+k+1tümü i, mesafe gereksinimi nedeniyle daha küçük veya eşit endekslerdir . Biz de tüm bu üzerinde yineleme gerekir, ancak asgari çünkü gereksizdir a[i+k+1]*a[j]üzerinde jsabit olarak ieşittir min(a[i+k+1]*max(a[j]), a[i+k+1]*min(a[j]))üzerinde minimum ve maksimum hem bakımından minimum alarak ürün (monotonicity nedeniyle a[j]iki olası için hesaplar belirtileria[i+k+1] monotonluğun iki olası yönünün veya eşdeğer olarak.)

Setinde yana a[j]değerlerine hangi üzerinde sadece burada optimize {a[0], ..., a[i]}basitçe bir eleman (tarafından büyümeler, hangi a[i]her tekrarında) i, biz sadece takip edebilir max(a[j])ve min(a[j])eğer bunları güncelleyerek tek değişkenlerle a[i]önceki optimum değerlerden daha büyük veya daha küçüktür. Bu yapılır back_maxve back_minkod örneğinde.

Yinelemenin ( i=0) ilk adımı döngüde atlanır ve bunun yerine değişkenlerin başlatılması olarak gerçekleştirilir.


3
@greybeard Onları saklamak zorunda değilim, çünkü optimal bir ürün için mümkün olan tek aday a[i+k+1]minimum ve maksimumdur.
Ceviz

Cevabınızın neden yanıtınızda çalıştığını açıklayabilir misiniz?
MinaHany

6

En hızlı olduğundan emin değilim .

İ <j - k'siz daha basit problem için , asgari ürün, en küçük ve en büyük iki elemandan gelen çiftlerin ürünleri arasındadır.

Yani, (aşağıdakiler çok karmaşıktır, ceviz cevabına bakınız )
(• k ≤ n ise balk
  • min'i başlat [0] * a [k + 1] 'ye ürün)

  • {} ve {a [ j ] ile başlayan iki dinamik minmax veri yapısını upToI ve ötesinde tutun kj }
  • her i için 0 ila n - k - 1
    • upToI'ye [ i ] ekle
    • ötesinde bir [ i + k ] kaldırmak

    • min ( upToI ) × min ( upIplusK dışında ), min ( upToI ) × max ( ötesindeIplusK ),
      max ( upToI ) × min ( ötesindeIplusK ) ve max ( upToI ) × maks ( ötesindeIplusK ) arasında yeni minimum ürün olup olmadığını kontrol edin

Bu en hızlı, en azından karmaşıklık açısından olmalıdır. O (n) zamanı ve depolanmasıdır.
smttsp

orijinal çözüm O (N ** 2) karmaşıklığına sahiptir, çözümünüzün karmaşıklığını nasıl tahmin edersiniz?
lenik

O (nlogn) süresi, O (n) boşluk (uygun minmax uygulamalar için)
greybeard

@greybeard. Neden n * logn zamanına ihtiyacınız var? Neden sadece içeren 4 * n dizisi tutarak değil minUpto, maxUpto, minBeyond, maxBeyond(İki tekrarlamalar oluşturabileceğiniz)? Ardından, üçüncü yinelemede, her bir dizin için mümkün olan en düşük çarpımı bulun.
smttsp

(@smttsp Bu ceviz çözümü yönünde alternatif bir adım olacaktır .)
greybeard

4

"Minimum büyüklük" için

2 "en küçük büyüklük" öğesini bulun, ardından (iki sıfır bulduktan veya dizinin tamamını aradıktan sonra), bunları çarpın.

Olmadan "düşük değere" için abs(i - j) > kkısmının

3 olasılık vardır:

  • en yüksek iki (en küçük büyüklük) negatif sayı

  • negatif olmayan en küçük iki sayı

  • en düşük (en büyük büyüklük) negatif sayı ve en yüksek (en büyük büyüklük) negatif olmayan sayı

Tüm 6 değeri arayabilir ve ürünleri anlayabilirsiniz ve hangisi sonunda en iyisidir.

Ancak; sıfır gördüğünüzde ilk 2 olasılık hakkında daha fazla bilgi sahibi olmanız gerekmediğini bilirsiniz; ve bir negatif sayıyı ve bir negatif olmayan sayıyı görür görmez sadece üçüncü olasılığı önemsediğinizi biliyorsunuz.

Bu, 3 durumlu bir sonlu durum makinesine yol açar - "tüm 3 olasılıkla ilgilen", "negatif bir sayı görülmedikçe cevap sıfırdır" ve "yalnızca son olasılıkla ilgilenir". Bu goto, sonlu durum makinesinin durumu değiştiğinde , ilmeklerden 2'si başka bir ilmenin ortasına ( ) atladığı 3 döngü kümesi olarak uygulanabilir .

Özellikle, belirsiz bir şekilde (denenmemiş) görünebilir:

   // 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;

İle "düşük değere" için abs(i - j) > kkısmının

Bu durumda hala 3 olasılığa sahipsiniz; ve aynı "sonlu durum makinesi ile 3 döngü" yaklaşımı ile çalışmasını sağlayabilir ama çok dağınık / çirkin olur. Bu durumda daha iyi bir alternatif, sıfır olup olmadığını ve bunların hepsinin negatif veya tümü pozitif olup olmadığını belirlemek için diziyi önceden tarayabilir; böylece ön taramadan sonra ya cevabın sıfır olduğunu bilebilir ya da sadece belirli bir olasılık için tasarlanmış bir döngü seçebilirsiniz.


1
Bu endeks farkında alt sınır k'yi nerede açıklar ?
greybeard

1
@ greybeard: (Bu bölümü kaçırdım) - kodun dikkate alınması için değiştirilmesi gerekir.
Brendan

Neden iki sıfıra ihtiyacınız var ?
TrentP

@TrentP: Argh - haklısın. Cevabın 0 veya negatif bir sayı olduğunu bilmek için bir sıfır yeterlidir.
Brendan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.