标签:
1
5
4 1 7 7 7
1 2 6
1 3 1
2 4 8
3 5 2
Case #1:
15
10
14
9
15
题目大意:
一棵树,节点有价值,树边有花费。
节点价值只能get到一次,树边花费每次经过都要消耗。
输出从节点1~n出发,分别能得到的最大净价值(节点收获-树边花费)
一眼树型dp
可能是做树dp做多了有感觉了,或者是因为训练计划上类似的题较多,。
首先考虑子问题:
节点1做根,输出最大价值。
令
这样转移其实就是
回到此题,需要求得其实是以1 ~ n为根的最大价值。
那么第一遍dfs求出上面的数组。
第二遍dfs当从u遍历v时,给v传递两个参数:
从u出发,不经过v,回到u的最大价值
从u出发,不经过v,不回u的最大价值
这样当遍历v时,考虑与
求不经过v回到u的最大价值好办,第一遍dfs的时候对路标记一下,如果经过了v,去掉v这条路上获得的最大价值即可。
但是求不经过v,不回到u的最大价值时,如果
因此考虑给dp再加一个记录,对于u而言,第一遍dfs找到回到u的最大价值和不会到u的最大以及次大价值。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int mxn = 112345;
struct Edge
{
int v,w,next,f;
};
Edge eg[mxn*2];
int head[mxn],val[mxn];
int ans[mxn];
int dp[mxn][3],nxt[mxn][3],tp;
void Add(int u,int v,int w)
{
eg[tp].v = v;
eg[tp].w = w;
eg[tp].next = head[u];
eg[tp].f = 0;
head[u] = tp++;
}
void dfs1(int u,int pre)
{
dp[u][0] = dp[u][1] = dp[u][2] = val[u];
nxt[u][0] = nxt[u][1] = nxt[u][2] = -1;
int v,w;
for(int i = head[u]; i != -1; i = eg[i].next)
{
v = eg[i].v;
if(v == pre) continue;
w = eg[i].w;
dfs1(v,u);
dp[u][1] += max(0,dp[v][0]-2*w);
dp[u][2] += max(0,dp[v][0]-2*w);
if(w <= dp[v][1])
{
int tmp = dp[u][0]+dp[v][1]-w;
if(tmp > dp[u][1])
{
if(dp[u][1] > dp[u][2])
{
dp[u][2] = dp[u][1];
nxt[u][2] = nxt[u][1];
}
dp[u][1] = tmp;
nxt[u][1] = i;
}
else if(tmp > dp[u][2])
{
dp[u][2] = tmp;
nxt[u][2] = i;
}
}
if(w*2 <= dp[v][0])
{
eg[i].f = 1;
dp[u][0] += dp[v][0]-w*2;
}
}
}
void dfs2(int u,int pre,int bk,int nbk)
{
bk = max(bk,0);
nbk = max(nbk,0);
//printf("u = %d bk = %d nbk = %d\n",u,bk,nbk);
int v,w;
ans[u] = max(dp[u][0]+nbk,dp[u][1]+bk);
int nbk1,nbk2,bbk;
for(int i = head[u]; i != -1; i = eg[i].next)
{
v = eg[i].v;
w = eg[i].w;
if(v == pre) continue;
bbk = dp[u][0];
nbk1 = dp[u][1];
nbk2 = dp[u][2];
if(eg[i].f)
{
bbk = bbk-dp[v][0]+w*2;
nbk1 = nbk1-dp[v][0]+2*w;
nbk2 = nbk2-dp[v][0]+2*w;
}
if(nxt[u][1] == i)
{
nbk1 = dp[u][1]-dp[v][1]+w;
}
if(nxt[u][2] == i)
{
nbk2 = dp[u][2]-dp[v][1]+w;
}
dfs2(v,u,bbk-2*w+bk,max(bbk+nbk-w,bk+max(nbk1,nbk2)-w));
}
}
int main()
{
int t,u,v,w,n;
scanf("%d",&t);
for(int z = 1; z <= t; ++z)
{
printf("Case #%d:\n",z);
memset(head,-1,sizeof(head));
tp = 0;
scanf("%d",&n);
for(int i = 1; i <= n; ++i) scanf("%d",&val[i]);
for(int i = 1; i < n; ++i)
{
scanf("%d%d%d",&u,&v,&w);
Add(u,v,w);
Add(v,u,w);
}
dfs1(1,1);
dfs2(1,1,0,0);
for(int i = 1; i <= n; ++i)
{
// printf("%d:\n",i);
// printf("back:%d\n",dp[i][0]);
// printf("nbk1:%d %d\n",nxt[i][1] == -1? -1: eg[nxt[i][1]].v,dp[i][1]);
// printf("nbk2:%d %d\n",nxt[i][2] == -1? -1: eg[nxt[i][2]].v,dp[i][2]);
printf("%d\n",ans[i]);
}
}
return 0;
}
【HDOJ 5834】Magic boy Bi Luo with his excited tree(树型DP)
标签:
原文地址:http://blog.csdn.net/challengerrumble/article/details/52213118