可以用树型dp,对于某一个结点,是否选择这个点进入集合,我们用dp数组表示,dp[i][0]dp[i][0]表示以ii结点为根的子树,不选取该结点时的最大独立集的数量,dp[i][1]dp[i][1]则表示以ii结点为根的子树,选取该结点时的最大独立集的数量,那么dfs构建这个dp数组以后,dp[s][1]dp[s][1]就是我们需要的答案了。

我们dfs遍历树,构建这个dp数组,每次先进入子树,算子树的dp,然后轮到自己,如果选取了这个结点本身,那么与该节点相邻的结点都不能选,dp[cur][1]+=dp[next][0]dp[cur][1]+=dp[next][0],如果没有选取该结点本身,对于子节点可以选也可以不选,我们取最大值即可,dp[cur][0]+=max(dp[next][1],dp[next][0])dp[cur][0]+=max(dp[next][1],dp[next][0])。因为一个结点可能有多个子节点,子节点的子树之间不会相邻,因此是累加。

#include<iostream>
#include<vector>
using namespace std;

int dp[500001][2];
vector<int> G[500001];

void dfs(int cur, int pre){
    for(int i = 0; i < G[cur].size(); i++){ //遍历所有子节点
        int next = G[cur][i];
        if(next == pre) //不能重复遍历
            continue;
        dfs(next, cur); //进入子树
        dp[cur][0] += max(dp[next][1],dp[next][0]); //不选该结点时,取子节点最大值
        dp[cur][1] += dp[next][0]; //该结点选了自己,不能选相邻的子结点
    }
}

int main(){
    int n, s;
    cin >> n >> s;
    for(int i = 1; i <= n; i++) //初始化每个结点都选取自己的情况
        dp[i][1] = 1;
    for(int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        G[u].push_back(v); //构建树
        G[v].push_back(u);
    }
    dfs(s, -1); //dfs遍历树
    cout << dp[s][1] << endl; 
    return 0;
}