POJ_1935(树的最优遍历,动规与DFS算法)

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值