菜鸡第一次接触树形dp这个东西,不过这个东西还是很好理解的(可能是因为模板题吧)
个人感觉,相比线性dp,树形dp的状态转移方程更加的直观,难点主要是在“树”的结构上比较麻烦。
题解:树的遍历是从根节点往子节点的方向深入,所以用dfs编程会容易一些。
这个题根据dp的解题思路,定义状态:
dp[i][0],表示不在这个结点入住时的最优解。
dp[i][1],表示在这个结点入住时的最优解。
转移状态方程:
(1)不选择当前结点,那么他的子节点可以选或者不选,所以我们要选择两者之间最大的,即
dp[v][0]+=max(dp[u][0],dp[u][1])
(2)选择当前结点,那么他的子节点必不可以选择,即
dp[v][1]+=dp[u][0]
之后我们要建立一棵树,主要是通过STL库里的vector来实现邻接表
再开始进行树的遍历,可以用dfs进行遍历,从根节点进行深搜。

注意一点要判断去往的一点是不是到达的这一点的父节点,如果不判断就死循环了呀呀呀呀

#pragma GCC optimize(3,"Ofast","inline")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <math.h>
#include <string>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <stdlib.h>
#include <vector>
const int MaxN = 0x3f3f3f3f;
const int MinN = 0xc0c0c00c;
typedef long long ll;
const int maxn=5e5+10;
const int mod = 1000007;
using namespace std;
bool visited[maxn];
vector<vector<int> > maps;

int n,s;
int dp[maxn][2];
void dfs(int u,int fa){
    dp[u][1]=1;
    for(int i=0;i<maps[u].size();i++){
        int v=maps[u][i];
        if(v==fa) continue;
        dfs(v,u);
        dp[u][1]+=dp[v][0];
        dp[u][0]+=max(dp[v][0],dp[v][1]);
    }
}

int main()
{
    cin>>n>>s;
    maps.resize(maxn);
    for(int i=0;i<n-1;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        maps[x].push_back(y);
        maps[y].push_back(x);
    }
    dfs(s,-1);
    cout<<dp[s][1]<<endl;
    return 0;
}