HDU 2196 Computer(树形dp)
题目大意: 求树中每个点到所有叶子节点的距离的最大值是多少。
思路: 这个题用两边dfs就可以,一遍是求当前点到子树那个方向上的最大值和次大值,另外一遍是父亲方向上的最大值。之所以要求子树方向上的次大值,是因为如果求当前点v的最长的距离的时候,子树里面的显而易见可以求出来,但是父亲方向上的就不确定了,如果父亲的取最大值的路径经过点v,那么这样求肯定就不对了,所以要求一个次大值,这时,用父亲的次大值加上父亲的权值和子树方向上的比较就行了。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 11000; struct Edge { int to, Next, len; }; Edge edge[maxn * 2]; int tot, head[maxn]; int maxlen[maxn], smaxlen[maxn];//maxlen当前节点的子树的最大值 smaxlen次大值 int maxid[maxn], smaxid[maxn];//maxid当前节点取最大值时都应的儿子编号, smaxid次大值时对应的编号 void init() { memset(head, -1, sizeof(head)); tot = 0; } void addedge(int u, int v, int len) { edge[tot].to = v; edge[tot].len = len; edge[tot].Next = head[u]; head[u] = tot++; } //找出每一个节点往下的最大值和次大值 void dfs1(int u, int fa)//求出以u为根节点的最大距离 { maxlen[u] = smaxlen[u] = 0; for (int i = head[u]; i != -1; i = edge[i].Next) { int v = edge[i].to; if (v == fa) continue; dfs1(v, u); if (smaxlen[u] < maxlen[v] + edge[i].len) { smaxlen[u] = maxlen[v] + edge[i].len; smaxid[u] = v; if (maxlen[u] < smaxlen[u]) { swap(maxlen[u], smaxlen[u]); swap(maxid[u], smaxid[u]); } } } } //找出从父节点过来和从当前点向下的最大值 void dfs2(int u, int fa) { for (int i = head[u]; i != -1; i = edge[i].Next) { int v = edge[i].to; if (v == fa) continue; if (v == maxid[u])//这里求的是v的而不是u的,和dfs1区分开,所以如果u取最大值时如果经过v的话,那么算从u过来的路径时要用次大值 { if (edge[i].len + smaxlen[u] > smaxlen[v]) { smaxlen[v] = edge[i].len + smaxlen[u]; smaxid[v] = u; if (maxlen[v] < smaxlen[v]) { swap(maxlen[v], smaxlen[v]); swap(maxid[v], smaxid[v]); } } } else//否则用最大值 { if (edge[i].len + maxlen[u] > smaxlen[v]) { smaxlen[v] = edge[i].len + maxlen[u]; smaxid[v] = u; if (maxlen[v] < smaxlen[v]) { swap(maxlen[v], smaxlen[v]); swap(maxid[v], smaxid[v]); } } } dfs2(v, u); } } int main() { int n; while (~scanf("%d", &n)) { init(); int a, b; for (int i = 2; i <= n; i++) { scanf("%d %d", &a, &b); addedge(i, a, b); addedge(a, i, b); } dfs1(1, 0); dfs2(1, 0); for (int i = 1; i <= n; i++) printf("%d\n", maxlen[i]); } return 0; }