Warum ich einen doppelten Thread erstellt habe
Ich habe diesen Thread nach dem Lesen der längsten ansteigenden Teilsequenz mit K zulässigen Ausnahmen erstellt . Mir wurde klar, dass die Person, die die Frage stellte, das Problem nicht wirklich verstanden hatte, weil sie sich auf einen Link bezog, der das Problem "Am längsten ansteigendes Subarray mit einer erlaubten Änderung" löst. Die Antworten, die er erhielt, waren für das LIS-Problem eigentlich irrelevant.
Beschreibung des Problems
Angenommen, ein Array A ist mit der Länge N gegeben . Finden Sie die am längsten ansteigende Teilsequenz mit K zulässigen Ausnahmen.
Beispiel
1) N = 9, K = 1
A = [3,9,4,5,8,6,1,3,7]
Antwort: 7
Erläuterung:
Die am längsten zunehmende Teilsequenz ist: 3,4,5,8 (oder 6), 1 (Ausnahme), 3,7 -> gesamt = 7
2) N = 11, K = 2
A = [5,6,4,7,3,9,2,5,1,8,7]
Antwort: 8
Was ich bisher gemacht habe ...
Wenn K = 1 ist, ist nur eine Ausnahme zulässig. Wenn der bekannte Algorithmus zur Berechnung der längsten ansteigenden Folge in O (NlogN) verwendet wird ( klicken Sie hier, um diesen Algorithmus anzuzeigen) , können wir den LIS für jedes Element des Arrays von A [0] bis A [N-1] berechnen A. Wir speichern die Ergebnisse in einem neuen Array L mit der Größe N . In Beispiel n.1 wäre das L-Array: L = [1,2,2,3,4,4,4,4,5].
Unter Verwendung der umgekehrten Logik berechnen wir das Array R , von dem jedes Element die aktuelle längste abnehmende Sequenz von N-1 bis 0 enthält.
Das LIS ist mit einer Ausnahme nur sol = max (sol, L [i] + R [i + 1]), wobei sol als sol = L [N-1] initialisiert wird . Also berechnen wir LIS von 0 bis zu einem Index i (Ausnahme), stoppen dann und starten ein neues LIS bis N-1 .
A=[3,9,4,5,8,6,1,3,7]
L=[1,2,2,3,4,4,4,4,5]
R=[5,4,4,3,3,3,3,2,1]
Sol = 7
-> Schritt für Schritt Erklärung:
init: sol = L[N]= 5
i=0 : sol = max(sol,1+4) = 5
i=1 : sol = max(sol,2+4) = 6
i=2 : sol = max(sol,2+3) = 6
i=3 : sol = max(sol,3+3) = 6
i=4 : sol = max(sol,4+3) = 7
i=4 : sol = max(sol,4+3) = 7
i=4 : sol = max(sol,4+2) = 7
i=5 : sol = max(sol,4+1) = 7
Komplexität: O (NlogN + NlogN + N) = O (NlogN)
weil die Arrays R, L NlogN-Zeit zum Berechnen benötigen und wir auch Θ (N) benötigen, um Sol zu finden .
Code für k = 1 Problem
#include <stdio.h>
#include <vector>
std::vector<int> ends;
int index_search(int value, int asc) {
int l = -1;
int r = ends.size() - 1;
while (r - l > 1) {
int m = (r + l) / 2;
if (asc && ends[m] >= value)
r = m;
else if (asc && ends[m] < value)
l = m;
else if (!asc && ends[m] <= value)
r = m;
else
l = m;
}
return r;
}
int main(void) {
int n, *S, *A, *B, i, length, idx, max;
scanf("%d",&n);
S = new int[n];
L = new int[n];
R = new int[n];
for (i=0; i<n; i++) {
scanf("%d",&S[i]);
}
ends.push_back(S[0]);
length = 1;
L[0] = length;
for (i=1; i<n; i++) {
if (S[i] < ends[0]) {
ends[0] = S[i];
}
else if (S[i] > ends[length-1]) {
length++;
ends.push_back(S[i]);
}
else {
idx = index_search(S[i],1);
ends[idx] = S[i];
}
L[i] = length;
}
ends.clear();
ends.push_back(S[n-1]);
length = 1;
R[n-1] = length;
for (i=n-2; i>=0; i--) {
if (S[i] > ends[0]) {
ends[0] = S[i];
}
else if (S[i] < ends[length-1]) {
length++;
ends.push_back(S[i]);
}
else {
idx = index_search(S[i],0);
ends[idx] = S[i];
}
R[i] = length;
}
max = A[n-1];
for (i=0; i<n-1; i++) {
max = std::max(max,(L[i]+R[i+1]));
}
printf("%d\n",max);
return 0;
}
Verallgemeinerung auf K Ausnahmen
Ich habe einen Algorithmus für K = 1 bereitgestellt. Ich habe keine Ahnung, wie ich den obigen Algorithmus so ändern kann, dass er für K Ausnahmen funktioniert. Ich würde mich freuen, wenn mir jemand helfen könnte.
( PS. Bei Bedarf kann ich Code für den K = 1-Algorithmus in C ++ bereitstellen.)
Antworten:
Diese Antwort wurde von meiner Antwort auf eine ähnliche Frage bei Computer Science Stackexchange geändert.
Das LIS-Problem mit höchstens k Ausnahmen lässt einen O (n log² n) -Algorithmus unter Verwendung der Lagrange-Relaxation zu. Wenn k größer als log n ist, verbessert sich dies asymptotisch auf dem O (nk log n) DP, was wir auch kurz erklären werden.
DP [a] [b] bezeichne die Länge der am längsten ansteigenden Teilsequenz mit höchstens b Ausnahmen (Positionen, an denen die vorherige Ganzzahl größer als die nächste ist), die am Element
ba enden . Dieser DP ist nicht am Algorithmus beteiligt, aber seine Definition erleichtert das Testen des Algorithmus.Der Einfachheit halber nehmen wir an, dass alle Elemente unterschiedlich sind und dass das letzte Element im Array das Maximum ist. Beachten Sie, dass dies uns nicht einschränkt, da wir einfach m / 2n zum m-ten Erscheinungsbild jeder Zahl hinzufügen und dem Array unendlich hinzufügen und eins von der Antwort subtrahieren können. Sei V die Permutation, für die 1 <= V [i] <= n der Wert des i-ten Elements ist.
Um das Problem in O (nk log n) zu lösen, behalten wir die Invariante bei, dass DP [a] [b] für b <j berechnet wurde. Schleife j von 0 nach k bei der j-ten Iteration, in der DP [a] [j] für alle a berechnet wird. Schleifen Sie dazu i von 1 nach n. Wir behalten das Maximum von DP [x] [j-1] über x <i und eine maximale Präfixdatenstruktur bei, die am Index i DP [x] [j] an Position V [x] für x <i und 0 hat an jeder anderen Position.
Wir haben DP [i] [j] = 1 + max (DP [i '] [j], DP [x] [j-1]), wobei wir über i', x <i, V [i '] <gehen V [i]. Das Präfixmaximum von DP [x] [j-1] gibt uns das Maximum der Terme des zweiten Typs, und das Abfragen der maximalen Präfixdatenstruktur für das Präfix [0, V [i]] gibt uns das Maximum der Terme des ersten Art. Aktualisieren Sie dann die Datenstruktur für das maximale Präfix und das maximale Präfix.
Hier ist eine C ++ - Implementierung des Algorithmus. Beachten Sie, dass diese Implementierung nicht davon ausgeht, dass das letzte Element des Arrays maximal ist oder dass das Array keine Duplikate enthält.
Nun kehren wir zum O (n log² n) -Algorithmus zurück. Wählen Sie eine Ganzzahl 0 <= r <= n. Definieren Sie DP '[a] [r] = max (DP [a] [b] - rb), wobei das Maximum über b, MAXB [a] [r] als das Maximum b übernommen wird, so dass DP' [a] [ r] = DP [a] [b] - rb und MINB [a] [r] ähnlich wie das Minimum wie b. Wir werden zeigen, dass DP [a] [k] = DP '[a] [r] + rk genau dann ist, wenn MINB [a] [r] <= k <= MAXB [a] [r]. Weiterhin werden wir zeigen, dass für jedes k ein r existiert, für das diese Ungleichung gilt.
Man beachte, dass MINB [a] [r]> = MINB [a] [r '] und MAXB [a] [r]> = MAXB [a] [r'], wenn r <r ', also wenn wir die beiden beanspruchten annehmen Als Ergebnis können wir eine binäre Suche nach dem r durchführen und O (log n) -Werte versuchen. Daher erreichen wir die Komplexität O (n log² n), wenn wir DP ', MINB und MAXB in O (n log n) berechnen können.
Dazu benötigen wir einen Segmentbaum, der die Tupel P [i] = (v_i, low_i, high_i) speichert und die folgenden Operationen unterstützt:
Bestimmen Sie bei gegebenem Bereich [a, b] den Maximalwert in diesem Bereich (Maximum v_i, a <= i <= b) und das Minimum niedrig und Maximum hoch gepaart mit diesem Wert im Bereich.
Stellen Sie den Wert des Tupels P [i] ein.
Dies ist einfach zu implementieren, da die Komplexität O (log n) pro Operation vorausgesetzt wird, dass Sie mit den Segmentbäumen vertraut sind. Einzelheiten finden Sie in der folgenden Implementierung des Algorithmus.
Wir werden nun zeigen, wie DP ', MINB und MAXB in O (n log n) berechnet werden. Fix r. Erstellen Sie den Segmentbaum, der anfänglich n + 1 Nullwerte enthält (-INF, INF, -INF). Wir behaupten, dass P [V [j]] = (DP '[j], MINB [j], MAXB [j]) für j kleiner als die aktuelle Position i ist. Setze DP '[0] = 0, MINB [0] = 0 und MAXB [0] auf 0, wenn r> 0, andernfalls auf INF und P [0] = (DP' [0], MINB [0], MAXB [ 0]).
Schleife i von 1 bis n. Es gibt zwei Arten von Teilsequenzen, die mit i enden: diejenigen, bei denen das vorherige Element größer als V [i] ist, und diejenigen, bei denen es kleiner als V [i] ist. Um die zweite Art zu berücksichtigen, fragen Sie den Segmentbaum im Bereich [0, V [i]] ab. Das Ergebnis sei (v_1, low_1, high_1). Setze off1 = (v_1 + 1, low_1, high_1). Fragen Sie für die erste Art den Segmentbaum im Bereich [V [i], n] ab. Das Ergebnis sei (v_2, low_2, high_2). Setzen Sie off2 = (v_2 + 1 - r, low_2 + 1, high_2 + 1), wobei wir die Strafe von r für das Erstellen einer Ausnahme erhalten.
Dann kombinieren wir off1 und off2 zu off. Wenn off1.v> off2.v set off = off1 und wenn off2.v> off1.v set off = off2. Andernfalls setzen Sie = (off1.v, min (off1.low, off2.low), max (off1.high, off2.high)). Dann setze DP '[i] = off.v, MINB [i] = off.low, MAXB [i] = off.high und P [i] = off.
Da wir bei jedem i zwei Segmentbaumabfragen durchführen, dauert dies insgesamt O (n log n). Es ist leicht durch Induktion zu beweisen, dass wir die korrekten Werte DP ', MINB und MAXB berechnen.
Kurz gesagt lautet der Algorithmus:
Vorverarbeiten, Werte so ändern, dass sie eine Permutation bilden, und der letzte Wert ist der größte Wert.
Binäre Suche nach dem richtigen r mit Anfangsgrenzen 0 <= r <= n
Initialisieren Sie den Segmentbaum mit Nullwerten, setzen Sie DP '[0], MINB [0] und MAXB [0].
Schleife von i = 1 nach n in Schritt i
Wenn MINB [n] [r] <= k <= MAXB [n] [r], geben Sie DP '[n] [r] + kr - 1 zurück.
Andernfalls ist, wenn MAXB [n] [r] <k ist, das korrekte r kleiner als das aktuelle r. Wenn MINB [n] [r]> k ist, ist das korrekte r größer als das aktuelle r. Aktualisieren Sie die Grenzen für r und kehren Sie zu Schritt 1 zurück.
Hier ist eine C ++ - Implementierung für diesen Algorithmus. Es findet auch die optimale Teilsequenz.
Wir werden nun die beiden Behauptungen beweisen. Das wollen wir beweisen
DP '[a] [r] = DP [a] [b] - rb genau dann, wenn MINB [a] [r] <= b <= MAXB [a] [r]
Für alle a, k existiert eine ganze Zahl r, 0 <= r <= n, so dass MINB [a] [r] <= k <= MAXB [a] [r]
Beides ergibt sich aus der Konkavität des Problems. Konkavität bedeutet, dass DP [a] [k + 2] - DP [a] [k + 1] <= DP [a] [k + 1] - DP [a] [k] für alle a, k. Dies ist intuitiv: Je mehr Ausnahmen wir machen dürfen, desto weniger hilft es uns, eine weitere zuzulassen.
Fix a und r. Setze f (b) = DP [a] [b] - rb und d (b) = f (b + 1) - f (b). Wir haben d (k + 1) <= d (k) aus der Konkavität des Problems. Angenommen, x <y und f (x) = f (y)> = f (i) für alle i. Daher ist d (x) <= 0, also d (i) <= 0 für i in [x, y). Aber f (y) = f (x) + d (x) + d (x + 1) + ... + d (y - 1), daher ist d (i) = 0 für i in [x, y). Daher ist f (y) = f (x) = f (i) für i in [x, y]. Dies beweist den ersten Anspruch.
Um das zweite zu beweisen, setze r = DP [a] [k + 1] - DP [a] [k] und definiere f, d wie zuvor. Dann ist d (k) = 0, daher ist d (i)> = 0 für i <k und d (i) <= 0 für i> k, daher ist f (k) wie gewünscht maximal.
Konkavität zu beweisen ist schwieriger. Einen Beweis finden Sie in meiner Antwort unter cs.stackexchange.
quelle