算是一题有点思维难度的题。
题意:
有一颗n个节点的树,现在要把这棵树分成n/2部分,每部分都由2个节点构成,给出n-1条边的边权。当2个点构成一个部分的时候,假设是从u->v,认为经过的边的边权和为需要的花费。问最少的花费和是多少。
思路:
一开始的时候有点没有思绪。
但是再纸上画一画可以发现一点特点。我们从叶子节点开始考虑。
假设当前节点为u,他有一个儿子为v,并且v为叶子节点。显然我们直接连接u、v作为一部分,花费为w(u,v)
假设有两个儿子v1,v2,我们需要连接u,v1,此时v2需要和u的父亲相连了。
我们总是以当前节点u作为考虑,可以发现,假设u的一个儿子为v,
如果v子树上的节点为偶数个,v子树自己就可以直接连完了,我们可以省略w(u,v)这条边。
如果v子树上的节点为奇数个,v子树需要有一个点和u相连。
那么我们可以通过dfs计算出当前节点u的和,只需要枚举u的儿子v,如果v子树有偶数个,不需要和u相连直接返回dfs(v),如果v子树是奇数个,dfs(v)再加一条w(u,v)就好了。
对于点u的儿子个数,我这里先跑了一遍dfs进行计算。
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=10004; int n; int tot,head[maxn],son[maxn]; struct node{ int to,next,w; }p[maxn*2]; void add(int u,int v,int w){ tot++; p[tot].to=v; p[tot].next=head[u]; p[tot].w=w; head[u]=tot; } void init() { tot=0; memset(head,-1,sizeof(head)); memset(son,0,sizeof(son)); } int dfs(int x,int fa){ int ans=1; for(int i=head[x];~i;i=p[i].next){ int y=p[i].to; if(y==fa)continue; ans+=dfs(y,x); } return son[x]=ans; } long long dfs1(int x,int fa){ long long ans=0; for(int i=head[x];~i;i=p[i].next){ int y=p[i].to; if(y==fa)continue; if(son[y]%2==0)ans+=dfs1(y,x); else ans=ans+dfs1(y,x)+p[i].w; } return ans; } int main() { int T; cin>>T; while(T--){ cin>>n; init(); for(int i=1;i<n;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } dfs(1,0); cout<<dfs1(1,0)<<endl; } return 0; }