牛客练习赛40C题

题目链接:https://ac.nowcoder.com/acm/contest/369/C

题目描述

小A给你了一棵树,对于这棵树上的每一条边,你都可以将它复制任意(可以为0)次(即在这条边连接的两个点之间再加一条边权相同的边),求所有可能新形成的图中欧拉路的最短长度。
欧拉路:从图中任意一个点开始到图中任意一个点结束的路径,并且图中每条边只通过恰好一次。

输入描述
第一行一个数 n ,表示节点个数
接下来 n-1 行,每行三个整数 u,v,w,表示有一条 u 到 v 边权为 w 的无向边
保证数据是一棵树

输出描述
一行一个整数,表示答案

输入样例
4
1 2 1
1 3 1
1 4 2

输出样例
5

说明
一种可能的方案为复制 <1,2,1> 这条边一次,欧拉路为4->1->2->1->3

数据范围
1≤n≤2×105
1≤ui,vi≤n
1≤wi≤104

思路:先在了解一个定义:欧拉回路:从图中任意一个点开始,结束时回到该点的路径,并且图中每条边只通过恰好一次。即是欧拉路起点和终点都为一个。
此时想要遍历整棵树并返回,答案即为所有权值之和的两倍。而欧拉路则不对起点和终点有要求,即起点到终点的路径只用计算一次。答案就是所有权值的两倍-数中两点距离最长值。
题目转化为求数的直径,即树上权值距离最长的两点。

第一种解法:两边dfs
假设s-t是最长路径,先从任意一点n开始找离这一点最远的一点m,那点m一定是s和t中的一点。在从m点出发找离这一点最远的点q,则m-q就是s-t。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include
#include
#include
#include
using namespace std;
typedef long long ll;
template <class T>
inline void read(T &n) {
T ans = 0;
char ch = '+';
int flag = 0;
while (ch<'0' || ch>'9') {
ch = getchar();
if (ch == '-')
flag = 1;
}
while (ch >= '0'&&ch <= '9') {
ans = ans * 10 + ch - 48;
ch = getchar();
}
n = flag == 1 ? -ans : ans;
}
vectorint, ll> >a[200100];
ll dis[200100];
int node;
ll ans;
void dfs(int x, int fa) {
for (int i = 0; i < a[x].size(); i++) {
if (a[x][i].first != fa) {
dis[a[x][i].first] = dis[x] + a[x][i].second;
if (ans < dis[a[x][i].first]) {
ans = dis[a[x][i].first];
node = a[x][i].first;
}
dfs(a[x][i].first, x);
}
}
}
int main() {
int n;
read(n);
int x, y;
ll z, sum = 0;
for (register int i = 1; i < n; i++) {
read(x), read(y), read(z);
a[x].push_back(pair<int, ll>(y, z));
a[y].push_back(pair<int, ll>(x, z));
sum += z * 2;
}
//cout << sum << endl;
ans = 0;
dis[1] = 0;
dfs(1, 0);
//cout << node << endl;
ans = 0;
dis[node] = 0;
dfs(node, 0);
printf("%lld\n", sum - ans);
//system("pause");
}

第二种解法: 树形dp

在以i为根节点的子树中,经过点i的最大直径是i到叶子节点的最大距离和次大距离之和。定义dp[i]为以i为根节点到叶子节点的最大距离,dp[i]=max(dp[i],dp[i的子节点j]+i到j的距离)。而每次更新前可以求得直径ans=max(ans,dp[i]+dp[j]+i到j的距离),此时dp[i]为没有包含节点j的最远距离,所以当前的dp[i]和dp[j]+i到j的距离就是最大距离和次大距离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
const long long mod = 1e8 + 7;
ll dp[maxn];
struct node {
int s, e, w, next;
}a[maxn * 2];
int head[maxn];
ll len, ans;
void add(int s, int e, int w) {
a[len].e = e;
a[len].w = w;
a[len].next = head[s];
head[s] = len++;
}
void dfs(int x, int fa) {
for (int i = head[x]; i != -1; i = a[i].next) {
int y = a[i].e;
if (y == fa)
continue;
dfs(y,x);
ans = max(ans, dp[x] + dp[y] + a[i].w);
dp[x] = max(dp[x], dp[y] + a[i].w);
}
}
int main() {
int n;
scanf("%d", &n);
memset(head, -1, sizeof(head));
len = 0, ans = 0;
int x, y, z;
ll sum = 0;
for (int i = 1; i < n; i++) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
sum += z * 2;
}
dfs(1, -1);
printf("%lld\n", sum - ans);
}

谢谢你请我吃糖果

你可能感兴趣的:(牛客练习赛40C题)