HDU5758
题意:求一棵树最少有几条链构成,求链的最短长度和。
题解
- 首先必须明确,最少的链一定是从叶子结点到叶子结点。
- 如果叶子结点有奇数个,偶数个配对,剩下的一个向外面的叶子配对。
- 如果叶子结点有偶数个,正好可以两两配对。但这并不是最优方案,比如u有偶数个儿子,儿子之间正好配对,但是u可能又作为一个叶子结点和其它结点配对。这样子会导致链数增多。所以可以增加长度来减小链数。即有两个儿子不配对,都和u外面的配对。所以假设u的父亲为fa,u--fa就要经过2次。如果是奇数,u--fa就经过1一次。
- 先dfs一遍,记录每条边的经过的次数,求和。如果是偶数个叶子结点,那么就是答案,因为正好两两配对。
- 如果是奇数个叶子结点,有叶子是应该孤立的,但我们是按偶数的方案去求的。比如root有son1和son2,son1有三个结点,son2有两个结点,我们让son2的两个结点都与外面的配对,即root--son2经过两边。实际上son2的一个结点应该是孤立的。所以第二遍dfs时,记录最大多余的次数,最后偶数次的方案减去它即可。
- 最后注意根节点是叶子结点的情况。
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 100000 + 10;
int n,leaf,ans,MAX,sz[N],in[N];
int ne[N<<1],first[N],to[N<<1],tim[N<<1],tot;
void add(int u,int v){
to[tot] = v;
ne[tot] = first[u];
first[u] = tot++;
}
void dfs(int u,int fa){
for(int i=first[u];i!=-1;i=ne[i]){
int v = to[i];
if(v == fa) continue;
dfs(v,u);
sz[u] += sz[v];
if(sz[v] & 1) tim[i] = 1;
else tim[i] = 2;
ans += tim[i];
}
if(sz[u] == 0) leaf++, sz[u] = 1;
}
void dfs2(int u,int fa,int now){
MAX = max(MAX,now);
for(int i=first[u];i!=-1;i=ne[i]){
int v = to[i];
if(v == fa) continue;
if(tim[i] == 1) dfs2(v,u,now-1);
else dfs2(v,u,now+1);
}
}
void Init(){
MAX = ans = leaf = tot = 0;
memset(sz,0,sizeof(sz));
memset(in,0,sizeof(in));
memset(tim,0,sizeof(tim));
memset(first,-1,sizeof(first));
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
Init();
for(int i=0;i<n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v); add(v,u);
in[v]++, in[u]++;
}
dfs(1,1);
if(in[1] == 1) leaf++;
if(leaf & 1){
dfs2(1,1,0);
printf("%d\n",ans - MAX);
}else printf("%d\n",ans);
}
return 0;
}