給一棵$N$個節點的樹,將最少點塗色,使得每個沒被塗色的點都至少跟一個塗色的點相鄰。
$N\le 10000$
這題有個專有名詞叫樹上最少獨立集。
可以考慮dp,令dp[i]
[0]: 該點塗色。
[1]: 該點不塗色,且子節點至少有一個有塗色。
[2]: 該點不塗色,且子節點也都不塗。
令節點1為根,答案就會是min(dp[1][0], dp[1][1]),dp[1][2]為不符合要求的,所以不能當答案。
接著是轉移,令v為i的所有子節點
$$dp[i][0]=1+\sum \min^2_{x=0}dp[v][x]$$
$$dp[i][2]=\sum dp[v][1]$$
剩下1的轉移,理想情況下
$$dp[i][1]=\sum min(dp[v][1], dp[v][0])$$
然而要考慮dp[v][0]的轉移數量,如果全部的轉移來源都是dp[v][1],那就得使其中一個變成dp[v][0],詳細看code(#
時間複雜度$\mathcal{O}(N)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
#include <bits/stdc++.h> using namespace std;
#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL); using ll = int64_t; const int INF = 0x3F3F3F3F;
signed main() { EmiliaMyWife int n; while(cin >> n) { vector<vector<int>> edge(n + 1); vector<array<ll, 3>> dp(n + 1); for(int i = 1, a, b; i < n; i++) { cin >> a >> b; edge[a].push_back(b); edge[b].push_back(a); } const function<void(int, int)> dfs = [&] (int u, int p) { dp[u][0] = 1; ll res = INF, cnt = 0; for(int v : edge[u]) { if(v == p) continue; dfs(v, u); dp[u][0] += *min_element(dp[v].begin(), dp[v].end()); dp[u][1] += dp[v][1]; dp[u][2] += dp[v][1]; if(dp[v][0] <= dp[v][1]) cnt++, dp[u][1] += dp[v][0] - dp[v][1]; else res = min(res, dp[v][0] - dp[v][1]); } if(!cnt) dp[u][1] += res; }; dfs(1, 1); cout << min(dp[1][0], dp[1][1]) << '\n'; } }
|