POJ_1935,树的最优遍历,结合动规与DFS,参考博客
#include <stdio.h>
#include <string.h>
#include <vector>
#define N 50005
using namespace std;
//某城市的道路,ed为连接的城市,val为道路长度,存放在gra[]中
struct E{
int ed,val;
E(int a,int b):ed(a),val(b){}
};
vector<E>gra[N]; //每个城市都有一个vector用于存放他的道路信息
//每个城市的:父节点、是否要被经过、从该城市出发经过其所有需要旅行的子节点城市所需走的距离
//其中,dp[k][0]是来回距离(即需要返回该城市),dp[k][1]是单程距离(不需要返回该城市)
int fa[N],flag[N],dp[N][2];
int n,root;
void dfs(int s,int f){ //DFS标记树中各城市s的父节点f
fa[s]=f;
for(int i=0;i<gra[s].size();i++){
int t=gra[s][i].ed;
if(fa[t]) continue; //若子节点的父亲已被标记则跳过,防止重复标记
dfs(t,s);
}
}
void dpt(int s){
for(int i=0;i<gra[s].size();i++){
int t=gra[s][i].ed;
//若该城市是当前城市的父节点(已旅行过)或不须旅行则跳过
if(t==fa[s] || flag[t]==0) continue;
dpt(t);
}
for(int i=0;i<gra[s].size();i++){
int t=gra[s][i].ed,v=gra[s][i].val;
if(t==fa[s] || flag[t]==0) continue;
//当前城市的单程距离为其每个需旅行的子城市中选择一个走单程,其余走双程
//min(当前子城市走单程(此时其他子城市已遍历完且回到父城市),
//当前子城市走双程(此时在那个走单程的子城市,仅仅在父城市走单程距离的基础上累加上该子城市双程距离))
dp[s][1]=min(dp[s][0]+dp[t][1]+v,dp[s][1]+dp[t][0]+2*v);
//当前城市的来回距离为其每个需旅行的子城市来回距离之和
dp[s][0]+=dp[t][0]+2*v;
}
}
void run(){
dfs(root,-1);
int m;
scanf("%d",&m);
flag[root]=1; //出发城市(树的根)被标记
for(int i=0;i<m;i++){
int a;
scanf("%d",&a);
while(!flag[a]){ //将要旅行的城市以及要到达该城市必须经过的城市(父节点)标记
flag[a]=1;
s=fa[a];
}
}
dpt(root);
printf("%d\n",dp[root][1]);
}
int main(){
while(scanf("%d%d",&n,&root)==2){ //正确读入数据,n是城市数,root是起点编号
memset(fa,0,sizeof(fa));
memset(flag,0,sizeof(flag));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
gra[i].clear();
for(int i=1;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
gra[a].push_back(E(b,c));
gra[b].push_back(E(a,c));
}
run();
}
return 0;
}