题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2196
题目大意:给一棵树,每条树边都有权值,问从每个顶点出发,经过的路径权值之和最大为多少?每条树边都只能走一次,n <= 10000,权值<=10^9
解题思路:比较复杂的树形DP,刚看到题目一点思路都没有,但一想金华赛区的那道B题,似乎有点相似,那题是先深搜一次看每个节点拥有多少子孙节点和子孙节点的权值和。这题要记录的信息比较多,状态转移也比较复杂。设定1为树根(其实每个节点当根都是一样的),这样树变成了有向树,每个节点都有若干个子分支,用一次深搜从叶子节点往上更新算出每个分支到叶子节点的最大权值和。此时得出了根节点到其他分支的最大权值和。得出上面的信息之后,怎么得到答案呢?其他节点的计算类似于根节点。从根节点的孩子出发,先找到根节点除当前分支(根->当前节点为一个分支)外其他分支的最大权值和,然后往上更新选择的这条分支权值和,也就是父亲节点找到的其他分支最大权值和+树边的权值,见Tree_DP()的第二个While循环。不断递归重复上述过程即可得解。
状态转移方程: dp[i] = max(dp[i],tree[i][j].sum)(j为i的子节点,这个tree[i][j].len是分支的权值和)
测试数据:
2
代码:
#include <stdio.h> #include <string.h> #define MAX 10001 #define max(a,b) (a)>(b)?(a):(b) struct node { int v,len,sum; node *next; }*head[MAX*2],tree[MAX*2]; __int64 dp[MAX]; int n,ptr,vis[MAX]; void Initial() { ptr = 1; memset(dp,0,sizeof(dp)); memset(vis,0,sizeof(vis)); memset(head,NULL,sizeof(head)); } void AddEdge(int x,int y,int len) { tree[ptr].v = y,tree[ptr].len = len; tree[ptr].next = head[x],head[x] = &tree[ptr++]; tree[ptr].v = x,tree[ptr].len = len; tree[ptr].next = head[y],head[y] = &tree[ptr++]; } void Dfs(int v) { vis[v] = 1; node *p = head[v]; while (p != NULL) { if (!vis[p->v]) { Dfs(p->v); dp[v] = max(dp[v],dp[p->v]+p->len); p->sum = dp[p->v] + p->len; } p = p->next; } } void Tree_DP(int pa,int son) { if (vis[son]) return; vis[son] = 1; int i,j,k,maxx = 0; node *p = head[pa]; while (p != NULL) { //找到父节点除son外其他分支的最大价值 if (p->v != son) maxx = max(maxx,p->sum); p = p->next; } p = head[son]; while (p != NULL) { if (p->v == pa) { //这一步至关重要,往上更新,重要才能保证每步都得到最优解 p->sum = p->len + maxx; break; } p = p->next; } p = head[son]; while (p != NULL) { //每次都更新当前节点,并往下递归计算,父节点会因为vis=1而不计算 dp[son] = max(dp[son],p->sum); Tree_DP(son,p->v); p = p->next; } } int main() { int i,j,k,a,b; while (scanf("%d",&n) != EOF) { Initial(); for (i = 2; i <= n; ++i) { scanf("%d%d",&a,&b); AddEdge(i,a,b); } Dfs(1); memset(vis,0,sizeof(vis)); node *p = head[1]; while (p != NULL) { Tree_DP(1,p->v); p = p->next; } for (i = 1; i <= n; ++i) printf("%I64d\n",dp[i]); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。