Shortest Path(树形DFS 思维)

Shortest Path

在这里插入图片描述

题目大意: 给你一颗带权树,保证有偶数个节点n,把这n个节点分成 n/2 对儿,每对儿两点之间的权值为 len,即为原来两点之间路径的边权和,求这 n/2 对儿的 len和 的最小值。

题解: 即为树,那几乎又离不开递归求解,维护答案。此题的关键在于对题目求解问题的转换和分类简化。很显然若要总和最小,那么在分对儿时 两点之间的经过边数不会超过两条 ,所以我们考虑一个 一个父节点 f 和 其孩子 x 之间的边是如何取舍的。那么在树形结构里就是:1、当这个 x 有奇数个孩子的时候,那么这些孩子相邻两个兄弟之间组合后,必会剩下一个与 x 组合,此时 x 便不能再和其父节点 f 组合了,那么父节点就相当于没有这个孩子,其之间的边也不能加上。2、同样的当这个 x 有偶数个孩子的时候, x 必然要和其父节点 f 组合才行,那么这个边就得加上。
总的来说 就是在从叶子节点往上递归时,记录不应该被选的边,最后用整个树的边权,减去这些不应该选的边权即可。

Code:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
const int N = 1e5+7;

int h[N],e[N*2],ne[N*2],w[N*2],idx; 	// 无向图
int in[N];
LL ans=0,vis=0;

void add(int a,int b,int v){		// 邻接表
	e[idx] = b,w[idx] = v, ne[idx] = h[a],h[a] = idx++;
}

void DFS(int x,int f,int k)	// x 是 f 的孩子,k 是之间的边权
{
	int sum=0;
	for(int i=h[x];i!=-1;i=ne[i]){
		int v = e[i],ww = w[i];
		if(v==f) continue;
		DFS(v,x,ww);
		sum += ww;
	}
	if(in[x]%2==0) ans += sum , in[f]-- , vis += k;	//此节点有奇数个孩子,其就父节点少一个孩子,其间的边不选
	else ans += sum ;
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,a,b,wi;
		cin>>n;
		memset(in,0,sizeof in);
		memset(h,-1,sizeof h);
		idx = 0;		//多组输入,初始化
		for(int i=0;i<n-1;i++){
			scanf("%d%d%d",&a,&b,&wi);
			add(a,b,wi);
			add(b,a,wi);
			in[a]++,in[b]++;
		}
		ans=0,vis=0;
		DFS(1,0,0);
		cout<<ans-vis<<endl;	//树边的总和减去不应该被选的边
	}
	return 0;
}

这里提供另一种角度的代码:

若要使两两之间边权最小,尽量不能选重边,也就是说尽可能在节点所在子树里寻找答案。
DFS统计每个子树大小,偶数不用处理,奇数加边权即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=1e4+10;
struct edge{
    ll t,v,w;
}e[maxn<<1];
ll T,n,tot,ans,hd[maxn];
void add(ll x,ll y,ll z){
    tot++;
    e[tot].t=hd[x];
    e[tot].v=y;
    e[tot].w=z;
    hd[x]=tot;
}
ll Dfs(ll u,ll fa,ll x){
    ll cnt=1;
    for(int i=hd[u];i!=0;i=e[i].t)
        if(e[i].v!=fa)
            cnt+=Dfs(e[i].v,u,e[i].w);
    if(cnt%2) ans+=x;
    return cnt;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>T;
    ll x,y,z;
    while(T--){
        memset(hd,0,sizeof(hd));
        ans=tot=0;
        cin>>n;
        for(int i=1;i<n;i++){
            cin>>x>>y>>z;
            add(x,y,z);
            add(y,x,z);
        }
        Dfs(1,0,0);
        cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值