地址:http://acm.hdu.edu.cn/showproblem.php?pid=2196
题意:有很多机器通过一棵树连在一起,每条边有一个权值,求每个机器距离最远的路径是多长。
mark:这是一个典型的起点不固定的树形dp。我们首先想到的肯定是枚举所有顶点,但是这样计算会有很多重复计算,我们做dp就是为了减少重复计算。
于是我们可以先把它当成一个以任意节点(一般都以编号为1的节点)当根节点做一次dp,dp[i]代表以i为根的子树的最大权值。做完一次后根节点(节点1)肯定已经是最优的了,那么我们现在要想办法更新其他的节点,假如现在要更新节点1的儿子节点son,那么我们首先要计算出除了1到son以外的别的1出发到儿子节点的权值的最大,然后更新一下son目前的值,代码中有注释。然后再从son节点继续往下更新,这样就不减少了很多重复计算,最后每个节点都是最优的情况。
废话不多说,看代码吧~
代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> const int N = 10010; struct node { int v,w,sum; node *next; }*head[N],tree[2*N]; int n,m,ptr; int vst[N],dp[N]; int max(int a, int b) {return a > b ? a : b;} void init() { ptr = 1; memset(vst, 0, sizeof(vst)); memset(dp, 0, sizeof(dp)); memset(head, 0, sizeof(head)); } void AddEdge(int x,int y, int z) { tree[ptr].v = y; tree[ptr].w = z; tree[ptr].next = head[x],head[x] = &tree[ptr++]; } void dfs(int v) //v代表父亲节点 { vst[v] = 1; node *p = head[v]; while (p != NULL) { if(vst[p->v]) {p = p->next; continue;} dfs(p->v); //p->v代表v的儿子节点 dp[v] = max(dp[v], dp[p->v]+p->w); p->sum = dp[p->v]+p->w; p = p->next; } } void tree_dp(int fa, int son) { if(vst[son]) return ; vst[son] = 1; int i,j,k,max1; node *p = head[fa]; max1 = 0; while(p != NULL) //找到除了儿子节点以外的节点权值最大 { if(p->v != son) max1 = max(max1, p->sum); p = p->next; } p = head[son]; while(p != NULL) //更新一下儿子节点(假定当根节点)的权值 { if(p->v == fa) { p->sum = p->w+max1; break; } p = p->next; } p = head[son]; while(p != NULL) //更新儿子节点的最终权值,同时把继续搜寻儿子的儿子节点…… { dp[son] = max(dp[son], p->sum); tree_dp(son, p->v); p = p->next; } } int main() { int i,j,k; while(~scanf("%d", &n)) { init(); for(i = 2; i <= n; i++) { scanf("%d%d", &j, &k); AddEdge(i, j, k); AddEdge(j, i, k); } dfs(1); memset(vst, 0, sizeof(vst)); node *p = head[1]; while(p != NULL) { tree_dp(1, p->v); p = p->next; } for(i = 1; i <= n; i++) printf("%d\n", dp[i]); } }