문제

$ n-1 $ << "> $ N-1 $ <"수학 용기 "> $ N $ vertices에서 최소 숫자를 찾아야합니다./ span> 가장자리가 $ k $ 이이 트리의 가장자리 가이 정점에 연결됩니다.

예를 들어 $ n= 9 $ $ k= 6 $ 이면 우리는 이것을 가지고 있습니다.나무 :

   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
.

$ \ mathrm {min}= 2 $

여야합니다.

어떤 생각?

도움이 되었습니까?

해결책

$ \ mathcal {o} (nk) $ DP 접근 방식이 있습니다.

옆에있는 정점을 선택하면 가장자리를 덮습니다. 임의의 정점 $ r $ 의 나무를 뿌리줍니다. $ dp [i] [b] [t] $ 을 노드 $ i 하위 트리에서 대부분의 $ T $ 노드에서 선택하여 덮일 수있는 $ . $ b= 0 $ 노드 $ i $ 을 선택할 수 없습니다.="수학 용기"> $ b= 1 $ 우리는 그것을 선택해야합니다.

이 DP를 계산하면 $ k $ 가장자리가 가장 작은 $ max (DP [r] [0], DP [r] [1] [T]) \ GEQ의 $ dp 클래스="수학 컨테이너"> $ t \ leq k $ 에만 $ dp $ 을 계산하는 것이 충분합니다. $ k 노드는 최소한 $ k $ 가장자리를 덮습니다.

DP를 계산하기 위해 재발을 부여하기 위해 우리는 먼저 배낭 - 기능을 제공합니다. $ K (v_ {1}, \ dots, v_ {m}) $

와 같은 배열이 되십시오.

\ begin {방정식 *} K (v_ {1}, \ dots, v_ {m}) [T]=max_ {T_ {1} + \ dots + t_ {m}= t} \ sum_ {j= 1} ^ {m} v_ { J} [T_ {J}] \ end {방정식 *}

$ k (k (v_ {1}, \ dots, v_ {m-1}), v_ {m})= k (v_ {1}, \ 점, v_ {m}) $ $ k (a, b) $ $ \ mathcal {o} (| a | \ cdot | b |) $ 시간. 따라서 $ k (v_ {1}, \ dots, v_ {m}) $ $ \ mathcal {o}를 찍습니다. (\ sum_ {i= 2} ^ {m} | v_ {i} | \ sum_ {j= 1} ^ {i-1} | v_ {j} |) $ 우리가 결합한 순서에 관계없이 세트가 있습니다. 우리가 첫 번째 $ k $ DP의 값만에 관심이있는 경우 복잡성은 $ \ MathCal {o} (\ sum_ {i= 2} ^ {m} | v_ {i} | \ min (k, \ sum_ {j= 1} ^ {i-1} | v_ {j} |)) $ < / span>

$ C_ {i} $ 노드 $ i $ 의 자식 세트가되도록하십시오. $ C_ {ij} $ $ j $ th $ i $ . 그때 \ begin {gatel *} DP [i] [0] [T]= K (v_ {1}, \ dots, v_ {| c_ {i} |}) [~] \\ DP [i] [1] [t]= | c_ {i} | + k (v '_ {1}, \ dots, v'_ {| c_ {i} |}) [T-1] \ end {gather *} 어디 \ begin {gatel *} 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 {gather *} 이 재귀로 답변을 계산하면 $ \ mathcal {o} (NK) $ 시간이 걸립니다. 비공식적으로 이것은 알고리즘의 과정에서 단일 요소 DPS를 전체 트리를 나타내는 DP로 결합하기 때문입니다. 우리는 대부분의 $ \ frac {n} {k} $ 수학 용기 "세트의 조합"> $ k $ 그리고 모든 요소는 대부분의 $ 2k $ 시간 ( $ x \ $ 비용이있는 경우) US $ | b | $ K (A, B) $ 을 계산할 때 $ k (A, B) $ ) $ k $ 의 세트로 $ \ mathcal {o} (k) ^ {2} \ frac {n} {k} + kn)=mathcal {o} (nk) $ . 이것은 쉽지만 유도로 공식화하기가 지루합니다.

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

다른 팁

간단한 해결책은 $ dp (n, 2, n) $ 을 사용하는 것입니다. $ DP (i, 0, j) $ $ \ leq j를 사용하여 얻을 수있는 최대 가장자리 수가 가능합니다. 노드 $ i $ , 노드 $ i $ 자체로 루트 컨테이너에 루트 노드> 정점 덮개에 있지 않습니다. $ dp (i, 1, j) $ 이 동일하도록하자 $ i $ 이 포함됩니다 버텍스 커버에서.

전환 자체는 분명하지는 않지만, 배낭과 유사한 방법을 사용하여 수행 할 수 있습니다. 노드 $ i $ 의 모든 아이들을 고려하십시오. $ DP (CH, 0, C) $ $ DP (CH, 1, C) $의 모든 값을 사용하십시오. 두 개의 개별 배낭에있는 항목으로 $ dp (i, 0) $ 및 전체 배열 $ dp (i, 1) $ . 항목 비용은 $ c $ 이고 값은 다음과 같습니다.

$ dp (i, 0) $ : $ dp (ch, 0, c) $ $ dp (ch, 0, c) $ ; $ DP (CH, 1, C) $ $ dp (CH, 1, C) + 1 $ < / span>.


$ dp (i, 1) $ : $ dp (ch, 0, c) $ $ dp (CH, 0, C) +1 $ ; $ DP (CH, 1, C) $ $ dp (CH, 1, C) + 1 $ < / span>.

전체 배열 $ dp (i, 0) $ $ dp (i, 1) $ 모든 $ kn (last, j) $ 의 값의 끝 값에서 직접 컨테이너 "> $ j $ ). 배낭에는 노드 당 $ o (\ # 하위 * n) $ 항목이 $ o (\ # 어린이)에서 실행됩니다. * n * n) 노드 당 $ . 따라서 솔루션의 총 복잡성은 $ o (n ^ 3) $ 입니다. 동일한 노드가 취해지지 않도록하는 두 항목을 방지하기 위해 전통적인 0-1 배낭을 약간 수정해야합니다. 이것은별로 어렵지 않습니다. 또한 $ DP (i, 1) $ 어레이를 계산할 때 노드 $ i $ 자체는 정점 덮개의 여분의 노드입니다.

이 하나보다 더 빨리 실행되는 솔루션이 있는지 확실하지만 나는 그것을 의심하지 않을 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 cs.stackexchange
scroll top