樹的最小支配集、最小點覆蓋、最大獨立集
最小支配集
定義:對於圖 \(G=(V,E)\) ,最小支配集指的是從 \(V\) 中取儘量少的結點組成一個集合,使得 \(V\) 中剩餘的點都與取出來的點有邊相連。
解法:定義 dp 各狀態意義如下:
- \(dp[u][0]\):結點 \(u\) 屬於支配集,並且 \(u\) 的子孫都被覆蓋了的情況下支配集中包含的最少結點個數;
- \(dp[u][1]\):結點 \(u\) 不屬於支配集,並且 \(u\) 的子孫都被覆蓋,\(u\) 也被不少於 1 個子結點覆蓋的情況下支配集中包含的最少結點個數;
- \(dp[u][2]\):結點 \(u\) 不屬於支配集,並且 \(u\) 的子孫都被覆蓋,\(u\)
狀態轉移方程如下:
-
\(dp[u][0]=1+\sum_{v\in son_u} min(dp[v][0],dp[v][1],dp[v][2])\) ;
-
\(if\)(u沒有子結點): \(dp[u][1] = INF\)
\(else\): \(dp[u][1] = inc+\sum_{v\in son_u}min(dp[v][0],dp[v][1])\)
對於 \(inc\) 有:
\(if(\exist v\in son_u,\ dp[v][0]\le dp[v][1]):\ inc=0\)
\(else:\ inc=min(\{dp[v][0]-dp[v][1], v\in son_u\})\)
-
\(dp[u][2]=\sum_{v\in son_u}dp[v][1]\) 。
程式碼實現:例題:nowcoder-24953 Cell Phone Network
#include <cstdio> #include <algorithm> using std::min; const int maxn = 10010, INF = 0x3f3f3f3f; int dp[maxn][3]; int head[maxn], to[maxn<<1], nex[maxn<<1], tot; void add_edge(int u, int v) { to[++tot] = v; nex[tot] = head[u]; head[u] = tot; } void dfs(int u, int fa) { dp[u][0] = 1; dp[u][1] = dp[u][2] = 0; int inc = INF; for (int i = head[u], v; i > 0; i = nex[i]) { v = to[i]; if (v == fa) continue; dfs(v, u); dp[u][0] += min(dp[v][0], min(dp[v][1], dp[v][2])); if (dp[v][0] <= dp[v][1]) inc = 0; else if (inc) inc = min(inc, dp[v][0] - dp[v][1]); dp[u][1] += min(dp[v][0], dp[v][1]); if (dp[u][2] != INF && dp[v][1] != INF) dp[u][2] += dp[v][1]; else dp[u][2] = INF; } dp[u][1] += inc; } int main() { int n; scanf("%d", &n); for (int i = 0, u, v; i < n - 1; i++) { scanf("%d %d", &u, &v); add_edge(u, v); add_edge(v, u); } dfs(1, -1); printf("%d\n", min(dp[1][0], dp[1][1])); return 0; }
最小點覆蓋
定義:對於圖 \(G=(V,E)\) ,最小點覆蓋指的是從 \(V\) 中取儘量少的結點組成一個集合 \(V'\),使得 \(E\) 中所有的邊 \((u,v)\) 都滿足 \(u\in V'\ or \ v\in V'\) 。
解法:定義 dp 各狀態的意義如下:
- \(dp[u][0]\):表示 \(u\notin V'\) ,並且以 \(u\) 為根的子樹中所有的邊都被覆蓋的情況下 \(V'\) 中的最少結點數量;
- \(dp[u][1]\):表示 \(u\in V'\),並且以 \(u\) 為根的子樹中所有的邊都被覆蓋的情況下 \(V'\) 中的最少結點數量。
狀態轉移方程如下:
- \(dp[u][0] = \sum_{v\in son_u}dp[v][1]\) ;
- \(dp[u][1]=1+\sum_{v\in son_u}min(dp[v][0],dp[v][1])\) 。
程式碼實現:例題 nowcoder-106060 Strategicgame
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::min;
const int maxn = 1510;
int dp[maxn][2];
int head[maxn], to[maxn<<1], nex[maxn<<1], tot;
void add_edge(int u, int v) {
to[++tot] = v;
nex[tot] = head[u];
head[u] = tot;
}
void dfs(int u, int fa) {
dp[u][0] = 0;
dp[u][1] = 1;
for (int i = head[u], v; i > 0; i = nex[i]) {
v = to[i];
if (v == fa) continue;
dfs(v, u);
dp[u][0] += dp[v][1];
dp[u][1] += min(dp[v][0], dp[v][1]);
}
}
int main() {
int n;
while (~scanf("%d", &n)) {
memset(head, 0, sizeof(head));
tot = 0;
for (int i = 0; i < n; i++) {
char s[15];
scanf("%s", s);
int u = 0, idx = -1, k = 0;
while (s[++idx] != ':') u = u * 10 + s[idx] - '0';
++idx;
while (s[++idx] != ')') k = k * 10 + s[idx] - '0';
for (int j = 0, v; j < k; j++) {
scanf("%d", &v);
add_edge(u, v);
add_edge(v, u);
}
}
dfs(0, -1);
printf("%d\n", min(dp[0][0], dp[0][1]));
}
return 0;
}
最大獨立集
定義:對於圖 \(G(V,E)\) ,最大獨立集指的是從 \(V\) 中取儘量多的結點組成一個集合,使得這些結點之間沒有邊相連。
解法:像最小點覆蓋一樣也是定義 \(u\) 是否屬於獨立集的兩個 dp 狀態,太簡單了不寫了。