POJ 1935 Journey

7 篇文章 0 订阅

给你一棵树,要访问树上的m个节点,并且最后不用返回根,所走的最短距离是多少

首先,可以把树上不用走到的地方剪掉,那么叶子节点都是要访问的点

要访问所有叶子节点,那么新的树上的每一条边都是有用的,并且只有一个叶子节点访问完后不用返回

考虑如果最后要返回根,那么就形成了一条欧拉路径,且每条边访问两次,既然只有一个叶子节点不用返回,那么这个叶子节点必然是最后访问的一个叶子节点,最后要返回根的话,最后一段路径就是这个叶子节点到根的路径,那么我们就只要求出欧拉路径的长度,再剪去一个离根最远的叶子节点的距离,就是答案了

思路很巧妙,我原来想的是找第一个分叉点,但忽略了分叉点下面可能还有分叉点,中间的处理是不同的


代码:

#include<iostream>
#include<memory.h>
#include<string>
#include<cstdio>
#include<algorithm>
#include<math.h>
#include<stack>
#include<queue>
#include<vector>
#include<map>
using namespace std;
const int MAX=50005;
struct node 
{
	int v,w,next;
}g[MAX*5];
int adj[MAX],dis[MAX],dp[MAX],e,n,m;
int flag[MAX],wanted[MAX];
void add(int u,int v,int w)
{
	g[e].v=v; g[e].w=w; g[e].next=adj[u]; adj[u]=e++;
}
int pre_dfs(int u,int fa)
{
	int i,v;
	int cnt=wanted[u];
	for(i=adj[u];i!=-1;i=g[i].next)
	{
		v=g[i].v;
		if(v==fa)
			continue;
		cnt+=pre_dfs(v,u);
	}
	flag[u]=cnt;
	return cnt>0;
}
void dfs(int u,int fa,int d)
{
	int i,v,tmp=0;
	dis[u]=d;
	dp[u]=0;
	for(i=adj[u];i!=-1;i=g[i].next)
	{
		v=g[i].v;
		if(v==fa||!flag[v])
			continue;
		dfs(v,u,d+g[i].w);
		dp[u]+=dp[v]+g[i].w*2;
	}
}
int main()
{
	int i,j,k,l,root;
	while(scanf("%d%d",&n,&root)!=EOF)
	{
		memset(adj,-1,sizeof(adj));
		memset(flag,0,sizeof(flag));
		memset(wanted,0,sizeof(wanted));
		e=0;
		for(l=1;l<n;l++)
		{
			scanf("%d%d%d",&i,&j,&k);
			add(i,j,k);
			add(j,i,k);
		}
		scanf("%d",&m);
		while(m--)
		{
			scanf("%d",&i);
			wanted[i]=1;
		}
		pre_dfs(root,-1);
		int maxx=-1;
		dfs(root,-1,0);
		for(i=1;i<=n;i++)
		{
			if(flag[i])
				maxx=max(maxx,dis[i]);
		}
		printf("%d\n",dp[root]-maxx);
	}
	return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值