给你一棵树,要访问树上的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;
}