HDU5758:树形DP

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_42264485/article/details/89305117