給你一棵樹,要訪問樹上的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;
}