HDU 5758 Explorer Bo【树形dp】

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5758

题意:

给定$n$个结点的一棵树,找到最少的链覆盖所有树边,求链最小长度和。
数据范围:$0 \le n \le 100000$

分析:

首先比较显然的是链的个数是可以确定的,最优的情况即是叶子结点两两一对,个数$\frac {leaf+1}{2}$
接下来考虑结点$u$,设其叶子结点个数为$son[u]$,边$edge$连接$u$及其子节点$v$。
若$son[v]$为奇数,那么最优情况一定是$v$的偶数个叶子结点自己跟自己配对,剩下一个通过$edge$伸出去和别人配对,这样$edge$被访问了一次。
若$son[v]$为偶数,如果全部是自己跟自己配对,那么此时的$u$相当于一个新的叶结点,要跟其他叶结点配对,显然会影响答案,所以只能是有两条通过$edge$延伸出去,此时$edge$被访问了两次。
若所有叶结点为偶数直接这样dfs一次就能出答案。若叶结点为奇数,链的一部分是可以省掉,那么如何计算省掉的最大部分呢?
这条链保留下来的部分后不需要和其他叶子节点相连,我们可以认为他是独立的,计算配对过程中可以不考虑这个叶结点,那么对于该节点的祖先来说他的叶子结点奇偶性发生改变,对应边的访问次数要相反。这样只要从根节点开始$dfs$一次,找到改变的最大值,然后与偶数情况下减一下即可。
【最后要注意判断所选根节点是否为叶结点!wa了好久

代码:

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*************************************************************************
> File Name: 5753.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/9/27 21:27:27
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 5, oo = 0x3f3f3f3f;
struct EDGE{
int to, next;
}edge[maxn * 2 + 5];
int head[maxn];
int tot = 0;
void addedge(int u, int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
int out[maxn];
int leaf;
int son[maxn];
int times[maxn * 2 + 5];
int ans;
void init()
{
tot = 0;
leaf = 0;
ans = 0;
memset(head, -1, sizeof(head));
memset(out, 0, sizeof(out));
memset(son, 0, sizeof(son));
memset(times, 0, sizeof(times));
}
int dfs(int u, int pa)
{
int tmp;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].to;
if(v == pa) continue;
dfs(v, u);
son[u] += son[v];
if(son[v] & 1) times[i] = times[i ^ 1] = 1;
else times[i] = times[i ^ 1] = 2;
ans += times[i];
}
if(son[u] == 0) leaf++, son[u] = 1;
}
int TMP, tmp;
void dfs2(int u, int pa, int now)
{
TMP = max(TMP, now);
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].to;
if(v == pa) continue;
if(times[i] == 1) tmp = -1;
else tmp = 1;
dfs2(v, u, now + tmp);
}
}
int main (void)
{
int T;scanf("%d", &T);
while(T--){
int n;scanf("%d", &n);
int x, y;
init();
for(int i = 0; i < n - 1; i++){
scanf("%d%d", &x, &y);
addedge(x, y);addedge(y, x);
out[x]++; out[y]++;
}
dfs(1, 1);
if(out[1] == 1) leaf++;
if(leaf & 1){
TMP = 0;
dfs2(1, 1, 0);
printf("%d\n", ans - TMP);
}else printf("%d\n", ans);
}
return 0;
}
文章目录
  1. 1. 题目链接:
  2. 2. 题意:
  3. 3. 分析:
  4. 4. 代码: