菜鸡第一次接触树形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; }