1. 程式人生 > 其它 >洛谷P2899 Cell Phone Network G 題解

洛谷P2899 Cell Phone Network G 題解

題目連結:https://www.luogu.com.cn/problem/P2899

題目大意:給你一棵樹,在樹中選擇一些節點,使得樹中的每個節點要麼是選擇的節點,要麼和至少一個選擇的節點相鄰。求:最少選擇節點個數。

解題思路:樹形DP。對於每個節點 \(u\),定義狀態:

  • \(f_{u,0}\):以 \(u\) 為根的子樹中,\(u\) 沒有選但是 \(u\) 的父節點選了(假設 \(u\) 有父節點)的情況下的最小選擇節點數;
  • \(f_{u,1}\):以 \(u\) 為根的子樹中,\(u\) 選擇了的情況下的最小選擇節點數;
  • \(f_{u,2}\):以 \(u\) 為根的子樹中,\(u\) 沒有選,但是 \(u\)
    的所有子節點中至少有一個選擇了的情況下的最小選擇節點數。

則,狀態轉移方程為(用 \(v\) 來表示左右 \(u\) 的子節點集合中的元素):

\(f_{u,0}\)

\(f_{u,0} = \sum\limits_{v} \min\{ f_{v,1}, f_{v,2} \}\)

  • 因為此時 \(u\) 沒有選,但是它的(即將到來的)父節點會選,所以對於 \(u\) 的子節點 \(v\) 來說,可以選(\(f_{v,1}\))也可以不選,但是不選的情況下因為 \(u\)\(v\) 都沒有選了,所以必須得保證 \(v\) 至少有一個子節點是選擇了的(對應狀態 \(f_{v,2}\)

\(f_{u,1}\)

\(f_{u,1} = \sum\limits_{v} \min\{ f_{v,0}, f_{v,1} \}\)

  • \(u\) 選了的情況下,它的子節點 \(v\) 可以選(對應狀態 \(f_{v,1}\))也可以不選,但是不選的時候對於 \(v\) 來說它的父節點是選擇了的(剛好對應 \(f_{v,0}\)

\(f_{u,2}\)

\(f_{u,2}\) 的計算比較特殊一點。

  • \(u\) 的子節點 \(v\) 中存在至少一個節點 \(v\) 滿足 \(f_{v,1} \le f_{v,0}\),則說明可以在選擇 \(v\) 的情況下達到 \(f_{u,0}\) 的最小是,則此時 \(f_{u,2} = f_{u,0}\)
  • 否則(所有 \(v\) 都是 \(f_{v,1} \gt f_{v,0}\)),則需要選擇一個 \(f_{v,1} - f_{v,0}\) 最小的一個 \(v\)(設為 \(v'\)),\(f_{u,2} = f_{u,0} - f_{v',0} + f_{v', 1}\)

當然,其中還存在一些不合法的情況,比如葉子節點的 \(f_{u,2}\) 就不可能存在,將其設為 \(+ \infty\) 即可(我程式碼中設為了 \(n\),因為所有的狀態都不會超過 \(n\))。

示例程式:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int n, f[maxn][3];
vector<int> g[maxn];
void dfs(int u, int p) {
    f[u][0] = f[u][2] = 0;
    f[u][1] = 1;
    bool flag = false;
    for (auto v : g[u]) {
        if (v != p) {
            dfs(v, u);
            f[u][0] += min(f[v][1], f[v][2]);
            f[u][1] += min(f[v][0], f[v][1]);
            if (f[v][1] <= f[v][2]) flag = true;
        }
    }
    if (flag) f[u][2] = f[u][0];
    else {
        f[u][2] = n;
        for (auto v : g[u]) {
            if (v != p) {
                f[u][2] = min(f[u][2], f[u][0] - f[v][2] + f[v][1]);
            }
        }
        if (f[u][2] == 0) f[u][2] = n;
    }
}
int main() {
    cin >> n;
    for (int i = 1; i < n; i ++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, -1);
    cout << min(f[1][2], f[1][1]) << endl;
    return 0;
}