您的位置:首页 > 其它

HDU 5834 Magic boy Bi Luo with his excited tree

2016-08-15 20:08 218 查看
题目:Magic boy Bi Luo with his excited tree

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5834

题意:给一棵树,在树的每个结点可以收获一定的积分,每一条边会消耗一定的积分,每个结点积分取一次便完,但每次路过一条边,都会消耗积分,问从每一个结点出发,最多能捞多少积分。N范围10万,样例数1万。

思路:

  不难的一道题,但细节较多,容易出错。

  树形DP。

  从i 点出发,大致可以分为向上 和 向下两种,先考虑向下的情况。可以想到,假设i 连着n 条边,那么如果要想把n 条边外的积分都拿到手,那么n 条边,有n-1 条边要走两次。那么,我们可以设置dp 如下:

  dp[i]:从i 出发,又回到i 的最优解

  fen[i][0]:从i 出发,不回到i 的最优解 ----- fen[i][1]:最终朝着哪个边离开

  fen[i][2]:从i 出发,不回到i 的次优解

  对i 结点,我们需要的信息就是上面4 个,一次dfs可以求出。

  对根节点rt,他的答案ans[rt] = fen[rt][0]。对于中间的某个结点x,ans[x] 是两种情况取最大,一:从i 出发,往上方走,又回到i ,再往下方走(不回来了),其中往下方走不回来就是fen[x][0],往上方走又回来(暂放)。二:从i 出发,往下方走,又回到i ,再往上方走(不回来),这里往下方走又回来就是dp[x],往上方走不会来(暂放)。

  上面这一步中dfs要传两个参数(up1,up2),up1表示往上方走不回来的最优解,up2表示往上方走又回来的最优解。现在我们来解决这两个参数,在x 点时,对于x 的某个孩子son ,e是x 和son 连接的边,假如这个son 就是fen[x][1],那么fen[x][2]-max(0,dp[son]-2*e)就代表了从x 出发,不往son 方向走又不回来的最优解,如果son 不是fen[x][1],那么这个最优解就是fen[x][0]-max(0,dp[son]-2*e),而up1代表了从x 出发,往上方走,不回来的最优解,那么要想两者都得到,要么往上方走回来(up2),要么往下方走回来(dp[x]-max(0,dp[son]-2*e)),这两个可以取最优解,就可以得到son 的up1、up2参数(up1要减去e,up2要减去2e)。

提供一组数据:

2
5
1 2 2 2 2
1 2 1
1 3 1
3 4 3
3 5 3
7
1 4 1 7 6 9 8
1 2 2
1 3 1
2 4 3
2 5 4
3 6 6
3 7 7

答案为:

1. 2 3 3 2 2

2. 7 8 7 10 10 10 8

我在倒二个10 弄了半天,一直显示9 ,搞了半天

AC代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
#define N 100010
vector<pair<int,int> > v
;
int w
;   //w[i] : 点i 的权值
int dp
;  //dp[i] : 从i 往下走,又回到i 的最大收获
int fen
[3];
/*
fen[i][0] : 从i 出发,不回到i 的最大收获
fen[i][1] : 从i 出发,不回到i 的最大收获 的 最后路径(就是不重复两次的那条)
fen[i][2] : 从i 出发,不回到i 的次大收获
*/
void dfs(int rt,int fa)
{
dp[rt]=w[rt];
for(int i=0;i<v[rt].size();i++)
{
if(v[rt][i].first==fa) continue;
dfs(v[rt][i].first,rt);
dp[rt] += max(0 , dp[v[rt][i].first] - 2*v[rt][i].second);
}
fen[rt][1]=-1;
fen[rt][0]=fen[rt][2]=w[rt];
for(int i=0;i<v[rt].size();i++)
{
int ad=v[rt][i].first;
int wa=v[rt][i].second;
if(ad==fa) continue;
int now = dp[rt] - max(0 , dp[ad]-2*wa) + max(0 , fen[ad][0]-wa);
// now 就是从这条路径出发不回来的收获
if(now>fen[rt][0])
{
fen[rt][2]=fen[rt][0];
fen[rt][0]=now;
fen[rt][1]=i;
}
else if(now>fen[rt][2]) fen[rt][2]=now;
}
}
int ans
;
void dfs2(int rt,int fa,int up1,int up2)
{//up1 : rt父结点出去不回来的最优解 up2 : rt父结点出去又回到父结点的最优解
ans[rt]=max(dp[rt]+up1,up2+fen[rt][0]);

for(int i=0;i<v[rt].size();i++)
{
int ad=v[rt][i].first;
int wa=v[rt][i].second;
if(ad==fa) continue;
int down1,down2;
if(fen[rt][1]==i) down1=max(0 , fen[rt][2] - max(0,dp[ad]-2*wa));
else down1=max(0 , fen[rt][0] - max(0,dp[ad]-2*wa));
down2=max(0 , dp[rt] - max(0,dp[ad]-2*wa));
down1=max(0 , max(up1+down2-wa,up2+down1-wa));
down2=max(0 , up2+down2-2*wa);
dfs2(ad,rt,down1,down2);
}
}
int main()
{
int t,n,x,y,z,cas=1;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
v[x].push_back(make_pair(y,z));
v[y].push_back(make_pair(x,z));
}
dfs(1,0);
dfs2(1,0,0,0);
printf("Case #%d:\n",cas++);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
for(int i=1;i<=n;i++) v[i].clear();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: