刚学树形dp表示WA哭了,调了半天才调出来。
树的直径:
对于一颗n个结点的无根树,找到一条最长路径,也就是找到两个点,让他们的距离最远。
学习树形dp之前,有一个操作,就是通过第一次dfs找到一个最深叶子结点,再通过这个点为根,去找另一个最深的叶子结点,就完成了最长链的操作,这个方法虽然简单,但是不够酷。
学习了树形dp,我们有一个操作,就是对于任意一个点为根(无向图以谁为根都无所谓),找到最大的子树,再找到第二大的子树,把他们加起来,再找到自己就可以了,这个就通过动态规划去逐步下放完成。
这题跟树的直径问题有点像,但是它是带权值的,略微有些不同。
root点的ans应该是该点的
dp[root]+dp[son]
dp[root]初始值为val[root],或者由其他儿子更新而来,这样就可以求出整一条链。
也就是自己这个点,连到最长的一个儿子,就是dp[root]的值,再通过另一个很大的儿子,去找到另一个叶子结点
然后更新root点的dp值
dp[root]=dp[son]+val[root]
本题要注意的点:
1.OI/ACM十年终是空,不开long long见祖宗
2.叶子结点同样要记得更新答案,不然通过90%的样例
以下是代码:
#include <bits/stdc++.h> using namespace std; const int N=100100; #define ll long long int n; ll val[N];//记录每一个结点的权值 ll dp[N]; vector <int> G[N]; ll ans=-1e12; void dfs(int root,int pre) { ans=max(ans,val[root]); dp[root]=val[root]; for(int i=0;i<G[root].size();i++) { int to=G[root][i]; if(to==pre) continue; dfs(to,root); ans=max(ans,dp[root]+dp[to]); dp[root]=max(dp[root],dp[to]+val[root]); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&val[i]); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } dfs(1,0); printf("%lld\n",ans); }