Min Vertex Cover per accedere ai bordi K in un albero
-
28-09-2020 - |
Domanda
Devo trovare il numero minimo di $ N $ vertici su un albero con $ N-1 $ Bordi, in modo che almeno $ k $ bordi di quell'albero sono collegati a questi vertici.
Ad esempio, se $ n= 9 $ e $ k= 6 $ e abbiamo questoAlbero:
Edges | Vertice #1 | Vertice #2
1 1 2
2 1 3
3 1 4
4 2 5
5 2 6
6 3 7
7 4 8
8 4 9
.
La risposta giusta dovrebbe essere $ \ MathRM {min}= 2 $ .
Qualche idea?
Soluzione
C'è una $ \ mathcal {o} (NK) $ approccio DP.
Chiama un bordo coperto se selezioniamo un vertice accanto ad esso. Radice l'albero in un vertice arbitrario $ r $ . Definisci $ DP [I] [B] [T] $ come numero massimo di bordi nel sottosuolo del nodo $ i $ che può essere coperto selezionando al massimo $ T $ nodi dal sottosuolo. Se $ B= 0 $ Non siamo autorizzati a selezionare il nodo $ I $ e se $ B= 1 $ Dobbiamo selezionarlo.
Se calcolamo questo DP, possiamo risolvere il problema, come il numero minimo di nodi per coprire $ k $ bordi è la più piccola classe $ T $ per quale $ max (DP [R] [0] [T], DP [R] [1] [T]) \ Geq k $ . Nota inoltre che è sufficiente calcolare solo la classe $ DP $ per $ t \ leq k $ , come qualsiasi $ k
Per dare alla recidiva per calcolare il DP, forniamo per la prima volta la funzione di zaino: let $ k (v_ {1}, \ dots, v_ {m}) $ Sii un array in modo tale da
\ begin {equation *} K (V_ {1}, \ Dots, V_ {M}) [T]=MAX_ {T_ {1} + \ DOTS + T_ {M}= T} \ Sum_ {J= 1} ^ {M} V_ { j} [T_ {J}] \ end {equation *}
Nota che $ k (k (v_ {1}, \ dots, v_ {m-1}), v_ {m})= k (v_ {1}, \ punti, v_ {m}) $ , e quella $ k (a, b) $ può essere calcolata direttamente dalla formula di cui sopra in $ \ Mathcal {o} (| A | \ clot | B |) $ tempo. Quindi calcolo di $ k (v_ {1}, \ dots, v_ {m}) $ prende $ \ \ mathcal {o} (\ sum_ {i= 2} ^ {m} | v_ {i} | \ sum_ {j= 1} ^ {i-1} | v_ {j} |) $ tempo indipendentemente dall'ordine che ci combiniamo I set in. Se siamo interessati a solo la prima $ k $ valori del DP, la complessità scende a $ \ mathcal {o} (\ sum_ {i= 2} ^ {m} | v_ {i} | \ min (k, \ sum_ {j= 1} ^ {i-1} | v_ {j} |)) $ < / span>
Let $ c_ {i} $ Sii il set di bambini del nodo $ i $ , e $ c_ {ij} $ essere la $ j $ th figlio di $ i $ . Poi \ Begin {raccogli *} DP [I] [0] [T]= k (V_ {1}, \ Dots, V_ {| c_ {i} |}) [T] \\ DP [I] [1] [T]= | c_ {i} | + K (V '_ {1}, \ Dots, V' _ {| c_ {i} |}) [T-1] \ end {raccogli *} Dove \ Begin {raccogli *} V_ {j} [T]=max (DP [c_ {ij}] [0] [T], DP [c_ {ij}] [1] [T] + 1) \\ V '_ {J} [T]=MAX (DP [C_ {IJ}] [0] [T], DP [C_ {IJ}] [1] [T]) \\ \ end {raccogli *} Calcolo della risposta con questa ricorsione prende $ \ mathcal {o} (NK) $ tempo. Informamente questo è perché nel corso dell'algoritmo, combiniamo DPS a elemento singolo in un DP che rappresenta l'intero albero. Facciamo al massimo $ \ frac {n} {k} $ combinazioni di serie di dimensioni $ k $ , e qualsiasi elemento ci costa al massimo $ 2k $ tempo (se elemento $ x \ in un costo di $ US $ | B | $ Tempo quando calcoli $ k (a, b) $ ) Prima di essere fusi in un set di dimensioni $ k $ , quindi la quantità totale di lavoro è al massimo $ \ mathcal {o} (k ^ {2} \ frac {n} {k} + kn)=mathcal {o} (NK) $ . Questo è facile ma noioso per formalizzare con induzione.
#include <iostream>
#include <vector>
#include <tuple>
using namespace std;
const int INF = (int)1e9 + 7;
vector<int> knapsack(const vector<int> a, const vector<int> b, int k) {
int n = a.size();
int m = b.size();
vector<int> c(n+m-1, -INF);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) c[i+j] = max(c[i+j], a[i] + b[j]);
}
if (c.size() > k) c.resize(k);
return c;
}
pair<vector<int>, vector<int>> dfs(int i, int p, int k, const vector<vector<int>>& g) {
vector<int> dp0 = {0, 0};
vector<int> dp1 = {-INF, (int)g[i].size() - (p != -1)};
for (auto t : g[i]) {
if (t == p) continue;
vector<int> dp0_t, dp1_t;
tie(dp0_t, dp1_t) = dfs(t, i, k, g);
int m = dp0_t.size();
vector<int> off0(m), off1(m);
for (int j = 0; j < m; ++j) off0[j] = max(dp0_t[j], dp1_t[j] + 1);
for (int j = 0; j < m; ++j) off1[j] = max(dp0_t[j], dp1_t[j]);
dp0 = knapsack(dp0, off0, k+1);
dp1 = knapsack(dp1, off1, k+1);
}
return {dp0, dp1};
}
int minCover(int k, const vector<vector<int>>& g) {
vector<int> dp0, dp1;
tie(dp0, dp1) = dfs(0, -1, k, g);
for (int i = 0;; ++i) {
if (max(dp0[i], dp1[i]) >= k) return i;
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
int n, k;
cin >> n >> k;
vector<vector<int>> g(n);
for (int i = 0; i < n-1; ++i) {
int a, b;
cin >> a >> b;
--a; --b;
g[a].push_back(b);
g[b].push_back(a);
}
int t = minCover(k, g);
cout << t << '\n';
}
. Altri suggerimenti
Una soluzione semplice è utilizzare lo stato $ DP (n, 2, n) $ . Let $ DP (i, 0, J) $ Sii il numero massimo di bordi che possiamo ottenere usando $ \ leq j $ nodi nel sottree radicato al nodo $ i $ , con nodo $ I $ non essere nella copertura del vertice. Let $ DP (I, 1, J) $ Sii lo stesso, tranne il nodo $ I $ è incluso nel coperchio del vertice.
La transizione stessa non è ovvia, ma può essere eseguita utilizzando un metodo simile allo zaino. Considera tutti i figli del nodo $ i $ . Utilizzare tutti i valori di $ DP (CH, 0, C) $ e $ DP (CH, 1, C) $ come elementi in due zaini separati: uno per calcolare l'array full snl $ DP (I, 0) $ e uno per calcolare l'array full matrice $ DP (I, 1) $ . I costi degli articoli sono uniformemente $ c $ , mentre i valori sono i seguenti:
Se calcolo $ DP (i, 0) $ : valore di $ DP (CH, 0, c) $ è $ DP (CH, 0, c) $ ; Valore di $ DP (CH, 1, C) $ è $ DP (CH, 1, C) + 1 $ < / span>.
Se calcolando $ DP (i, 1) $ : valore di $ DP (CH, 0, c) $ $ DP (CH, 0, C) +1 $ ; Valore di $ DP (CH, 1, C) $ è $ DP (CH, 1, C) + 1 $ < / span>.
Possiamo ottenere gli array completi $ DP (I, 0) $ e $ DP (I, 1) $ direttamente dai valori di fine dei cali del raccoglitore (cioè i valori di $ kn (ultimo, j) $ per tutte le momperette Contenitore "> $ J $ ). The Knapsack ha $ o (\ # Children * n) $ Articoli per nodo ed esegue in $ o (\ # # Bambini * n * n) $ per nodo. Pertanto, la complessità totale della soluzione è $ o (n ^ 3) $ . Si prega di notare che dovrai modificare leggermente il tradizionale zaino 0-1 per prevenire due elementi che rappresentano lo stesso nodo da essere preso; Questo non è molto difficile. Inoltre, quando si calcola la classe $ DP (I, 1) $ Array, si prega di notare che il nodo $ I $ per sé è un nodo extra sul coperchio del vertice.
Sono noto sicuro che se ci sono soluzioni in esecuzione in tempo più velocemente di questa, ma non ne dubiterei.