原题目是TYVJ的,但是现在tyvj评测机莫名鬼畜,于是我就把数据弄了下来,并且附上评测工具~
题目及数据地址——————–>百度网盘
评测工具(柠檬Lemon)——–>同样是百度网盘呢
先说一下Lemon的配置吧,选择本地的g++路径之后,创建本地的一个测试文件夹,其他的就和cena差不多啦~,lemon没有cena的一些bug,比cena好用呢~
不过lemon默认逐字比较,需要手动设置一下忽略行末空格和文末回车
Line
Time Limit: 1000MS
Memory Limit: 32768KB
【问题描述】
求出树上一条链,使得这条链上各边的权值和最大。
【输入文件】
输入文件line.in的第一行是一个整数N(2<=N<=100000),表示是n个节点的树。
之后N-1行,每行3个整数,a,b,c,表示a节点和b节点相邻,且边权值为c(绝对值小于等于1000)
【输出文件】
输出文件line.out包括一行,这一行只包含一个整数,就是最大的权值和。
【样例输入】
5
1 2 2
1 3 3
2 4 5
2 5 -1
【样例输出】
10
题解
如果你觉得这题是求树的直径,那么你就错了,因为这是带权的树,而且还是带负权!
所以这题我们要处理的是最长的树上链,并不是树上直径。
这样怎么办呢?树形DP?总之看见DP就头疼,那么我们就想办法绕过DP,不用DP来求解。
(下面思想来自黄学长博客,由于博文太多没找到原文……在此特声明一下)
开两个数组,f1,f2;f1表示当前点子树的最长链,f2表示当前节点子树的次长链,于是就有了类似DP的更新:
if(f1[u] < f1[v] + edges[i].dist)
{
f2[u] = f1[u];
f1[u] = f1[v] + edges[i].dist;
}
else f2[u] = max(f2[u],f1[v]+edges[i].dist);
但是这样比树形DP要简单多了(或许这就是树形DP我并不清楚DP的广义概念%……)
这样每次取max来更新ans,最后输出ans就是正解了~
下面附上完全代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int size = 233333;
struct Edge{int to,dist;}edges[size];
int f1[size],f2[size],head[size],next[size],tot;
void build(int f,int t,int d)
{
edges[++tot].to = t;
edges[tot].dist = d;
next[tot] = head[f];
head[f] = tot;
}
int ans = 0;
void dfs(int u,int fa)
{
for(int i = head[u];i;i = next[i])
{
int v = edges[i].to;
if(v == fa) continue;
dfs(v,u);
if(f1[u] < f1[v] + edges[i].dist)
{
f2[u] = f1[u];
f1[u] = f1[v] + edges[i].dist;
}
else f2[u] = max(f2[u],f1[v]+edges[i].dist);
}
ans = max(ans,f1[u]+f2[u]);
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1; i< n;i ++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
build(a,b,c);
build(b,a,c);
}
dfs(1,0);
printf("%d\n",ans);
return 0;
}
/*
5
1 2 2
1 3 3
2 4 5
2 5 -1
*/
蒟蒻Orz各位大神们~