題解:
通過畫圖觀察,以及貪心考慮得出一個結論,從一個點出發,如果要回到原來的點,則所有邊都要訪問兩次;而在這 道題中訪問到最後一個點則立即停下來,那麼最短路程和就是最後走到離這個點最遠的那個點停下,即所有邊的長度 * 2-這個點走到離它最遠點的距離。
而由於有多個出發點,那是我們利用從樹的直徑的性質,也就是在一棵樹上的一個點的最遠點一定是某一條樹的直徑 的端點,於是我們只需求出某一條樹的直徑,那麼這條直徑的2個端點一定有一個是每個點的最遠點。
我們先隨便選擇一個點,使用最短路徑算法或者dfs,找到某一條直徑的一個端點,再用這個端點再跑一遍最短路徑算法或dfs,找到直徑的另一個端點。然後對於每個可以作爲開始點的點,找到離他們最遠點的距離。取最大值,最 短路程即等於所有路徑長的兩倍減去這個最大值。
代碼如下:
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<queue>
#include<string>
#include<vector>
#define MAX 100005
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
int n;//n爲頂點數
int visit[MAX],dis[MAX],flag[MAX],maxdis[MAX];
struct edge{//存儲邊
int to;//邊的指向
int cost;//邊的權值
};
vector<edge> adj[MAX];
typedef pair<int,int> p; //first是最短距離,second是頂點的編號
struct cmp{//優先隊列的cmp是一個類
bool operator()(p a,p b) {
return a.first>b.first;
}
};
void dijkstra(int s){
for(int i=0;i<MAX;i++){
visit[i]=0;
dis[i]=INF;
}
dis[s]=0;
priority_queue<p,vector<p>,cmp > q;
q.push(p(0,s));//將源結點放入優先隊列
while(!q.empty()){
p top=q.top();
q.pop();
int u=top.second;
if(visit[u]==0){
visit[u]=1;
for(int i=0;i<adj[u].size();i++){
edge e=adj[u][i];
if(dis[e.to]>dis[u]+e.cost){
dis[e.to]=dis[u]+e.cost;
q.push(p(dis[e.to],e.to));
}
}
}
}
}
int main(){
scanf("%d",&n);
int a,b,len,tot=0;
for(int i=0;i<n-1;i++){
cin>>a>>b>>len;
edge e;
e.to=b;
e.cost=len;
adj[a].push_back(e);//adj[a]是從頂點a出發的邊的集合
e.to=a;
adj[b].push_back(e);
tot+=len;
}
bool tag=false;
for(int i=1;i<=n;i++){
scanf("%d",&flag[i]);
if(flag[i]) tag=true;
}
if(!tag){
printf("-1");
return 0;
}
dijkstra(1);
int ends,maxl=-1;
for(int i=1;i<=n;i++){
if(dis[i]>maxl){
maxl=dis[i];
ends=i;
}
}
dijkstra(ends);
maxl=-1;
for(int i=1;i<=n;i++){
maxdis[i]=dis[i];
if(dis[i]>maxl){
maxl=dis[i];
ends=i;
}
}
dijkstra(ends);
maxl=-1;
for(int i=1;i<=n;i++){
if(dis[i]>maxdis[i]){
maxdis[i]=dis[i];
}
if(flag[i]&&maxdis[i]>maxl){
maxl=maxdis[i];
}
}
int res=2*tot-maxl;
printf("%d",res);
return 0;
}