Il modo più veloce per trovare un prodotto minimo di 2 elementi dell'array contenenti oltre 200000 elementi


13

Ho un array a[n]. Il numero nè stato inserito da noi. Devo trovare il prodotto minimo di a[i]e a[j]se:

1) abs(i - j) > k

2) a[i] * a[j]è ridotto a icona

Ecco la mia soluzione (molto ingenua):

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

Ma voglio sapere se esiste un modo più veloce per trovare un prodotto minimo a distanza?


7
Perché non dovrei #include <bits / stdc ++. H>? e C ++ forniscono solo una matrice a lunghezza variabile per estensione del compilatore. Perché non stai usando std::vector? @Scheff - l'ordinamento distruggerebbe le relazioni "a distanza" originali.
David C. Rankin il

3
Almeno il controllo if (i!=j) if (abs(i - j) > k)può essere eliminato. Basta avviare il ciclo interno a i + k + 1: for (ll j = i + k + 1; j < n; ++j). Il controllo con firstpuò essere eliminato anche se mnè inizializzato prima, ad es mn = a[0] * a[k + 1];. Con . (Forse, inizialmente kdovrebbe essere verificato nper rendere questo a prova di proiettile.) Ma è ancora O (N²). Questo deve essere fattibile più velocemente ...
Scheff,

2
@PaulMcKenzie Mostra una query con non meno di due risultati utili tra i primi dieci per un prodotto minimo con distanza indice (o massima).
Greybeard

1
@PaulMcKenzie "Probabilmente ci sono centinaia, se non migliaia di collegamenti URL che mostrano le risposte a questa domanda." - per favore condividi almeno tre di questi URL.
עדלעד ברקן

2
Da dove viene questa domanda? Non sembra qualcosa appena inventato dal nulla. Non sarei sorpreso se provenisse da uno di quei siti "giudice online". In tal caso, su tali siti sono probabilmente lunghe discussioni sulla risoluzione del problema, se non soluzioni complete.
PaulMcKenzie,

Risposte:


12

Supponendo che vi sia almeno una coppia di elementi che soddisfi le condizioni e che non vi sia una moltiplicazione di due elementi in esso trabocca, ciò può essere fatto nel Theta(n-k)tempo e Theta(1)nello spazio peggiori e nel migliore dei casi, con qualcosa del genere:

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;

Questo è ottimale in termini di complessità asintotica nel caso peggiore sia per il tempo che per lo spazio perché il prodotto ottimale può essere almeno a[0]con uno qualsiasi degli n-(k+1)elementi in lontananza k+1, quindi almeno gli n-(k+1)interi devono essere letti da qualsiasi algoritmo che risolva il problema.


L'idea alla base dell'algoritmo è la seguente:

Il prodotto ottimale utilizza due elementi di a, supponiamo che siano a[r]e a[s]. Senza perdita di generalità possiamo supporre che s > rdal momento che il prodotto è commutativo.

A causa delle restrizioni abs(s-r) > kquesto implica che s >= k+1. Ora, sciascuno degli indici potrebbe soddisfare questa condizione, quindi ripetiamo questi indici. Questa è l'iterazione inel codice mostrato, ma viene spostata k+1per comodità (non importa davvero). Per ogni iterazione dobbiamo trovare il prodotto ottimale che coinvolge i+k+1come indice più grande e confrontarlo con la migliore ipotesi precedente.

I possibili indici da abbinare i+k+1sono tutti gli indici più piccoli o uguali a icausa della distanza richiesta. Dovremmo iterare anche su tutti questi, ma ciò non è necessario perché il minimo di a[i+k+1]*a[j]over ja fixed iè uguale a min(a[i+k+1]*max(a[j]), a[i+k+1]*min(a[j]))causa della monotonicità del prodotto (prendendo il minimo rispetto a entrambi i a[j]conti di minimo e massimo per i due possibili segni a[i+k+1]o equivalenti delle due possibili direzioni della monotonicità.)

Poiché l'insieme di a[j]valori su cui ottimizziamo qui è giusto {a[0], ..., a[i]}, che semplicemente aumenta di un elemento ( a[i]) in ogni iterazione di i, possiamo semplicemente tenere traccia di max(a[j])e min(a[j])con singole variabili aggiornandole se a[i]è maggiore o minore dei precedenti valori ottimali. Questo viene fatto con back_maxe back_minnell'esempio di codice.

Il primo passaggio dell'iterazione ( i=0) viene ignorato nel ciclo e invece eseguito come inizializzazione delle variabili.


3
@greybeard Non ho bisogno di tenerli in giro, perché gli unici candidati possibili per un prodotto ottimale a[i+k+1]sono il minimo e il massimo.
noce,

potresti spiegare perché l'algoritmo funziona nella tua risposta?
MinaHany

6

Non sono sicuro del più veloce .

Per il problema più semplice senza i <j - k , il prodotto minimo è tra i prodotti di coppie dei due elementi più piccoli e più grandi.

Quindi, (quanto segue è troppo complicato, vedi la risposta di noce )
(• balk se k ≤ n
  • inizializza minProdotto in uno [0] * a [k + 1])

  • mantenere fino a due strutture dati minmax dinamiche e oltre IplusK a
    partire da {} e {a [ j ] | kj }
  • per ogni i da 0 a n - k - 1
    • aggiungi un [ i ] a upToI
    • rimuovi un [ i + k ] da beyondIplusK
    • verifica la presenza di un nuovo prodotto minimo tra
      min ( upToI ) × min ( beyondIplusK ), min ( upToI ) × max ( beyondIplusK ),
      max ( upToI ) × min ( beyondIplusK ) e max ( upToI ) × max ( beyondIplusK )

Questo dovrebbe essere il più veloce, almeno per quanto riguarda la complessità. È O (n) tempo e memoria.
smttsp,

la soluzione originale ha la complessità O (N ** 2), come valuta la complessità della tua soluzione?
Lenik,

O (nlogn) tempo, O (n) spazio (per implementazioni minmax adatte)
greybeard

@greybeard. Perché hai bisogno di n * logn time. Perché non è sufficiente mantenere una matrice 4 * n che contiene minUpto, maxUpto, minBeyond, maxBeyond(è possibile creare in due iterazioni)? Quindi, nella terza iterazione, per ciascun indice, trova la moltiplicazione minima possibile.
smttsp,

(@smttsp Sarebbe un passo alternativo nella direzione della soluzione di noce .)
greybeard

4

Per "magnitudo minima"

Trova i 2 elementi di "grandezza minima", quindi (dopo aver trovato due zeri o aver cercato l'intero array), moltiplicali.

Per "valore più basso" senza il abs(i - j) > k parte

Vi sono 3 possibilità:

  • i due numeri negativi più alti (di minima grandezza)

  • i due numeri non negativi (magnitudo minima) più bassi

  • il numero negativo più basso (magnitudine maggiore) e il numero non negativo più grande (magnitudine maggiore)

Puoi cercare tutti e 6 i valori e capire i prodotti e quale è il migliore alla fine.

Però; appena vedi uno zero sai di non aver bisogno di sapere di più sulle prime 2 possibilità; e appena vedi un numero negativo e un numero non negativo, sai che ti importa solo della terza possibilità.

Questo porta a una macchina a stati finiti con 3 stati: "preoccuparsi di tutte e 3 le possibilità", "la risposta è zero a meno che non si veda un numero negativo" e "preoccuparsi solo dell'ultima possibilità". Questo può essere implementato come un insieme di 3 loop, in cui 2 dei loop saltano in ( goto) al centro di un altro loop quando lo stato (della macchina a stati finiti) cambia.

In particolare, potrebbe apparire qualcosa di vagamente simile (non testato):

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

Per "valore più basso" con la abs(i - j) > kparte

In questo caso hai ancora le 3 possibilità; e potrebbe farlo funzionare con lo stesso approccio "3 loop con macchina a stati finiti" ma diventa troppo disordinato / brutto. In questo caso è probabile che un'alternativa migliore esegua la scansione preliminare dell'array per determinare se vi sono zeri e se sono tutti negativi o tutti positivi; in modo che dopo la pre-scansione sia possibile sapere che la risposta è zero o selezionare un loop progettato solo per la specifica possibilità.


1
Da dove viene questa differenza per il limite inferiore k sull'indice?
Greybeard

1
@greybeard: No (ho perso quella parte) - il codice dovrebbe essere modificato per tenerne conto.
Brendan,

Perché avresti bisogno di due zeri?
TrentP

@TrentP: Argh - hai ragione. Uno zero è sufficiente per sapere che la risposta è 0 o un numero negativo.
Brendan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.