poj 1935 树形dp,

题意:一个人在m城镇,想参观这座城的k的城镇,求走过的最短路。题目链接

题解:假设从m点出发,最后回到m点,则 len为所有点的权值和乘以2,如果不会到m点,应该停留在叶子节点,这个以节点一定离m最远的点,在这条最远的路上,只会走一遍。所以最后结果应为  len 减去离m最远的距离。

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
#define FF  freopen("Input.txt","r",stdin)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define inf 1000020
const int N=50010;
struct Graph
{
    int head[N*2],val[N*2],pnt[N*2],next[N*2],tot;
    void init()
    {
        tot=0;
        mem(head,-1);
    }
    void add(int u,int v,int w)
    {
        pnt[tot]=v;
        val[tot]=w;
        next[tot]=head[u];
        head[u]=tot++;
    }
}G;
int tot,dis[N],vis[N],have[N],len;
void dfs1(int u) //标记去想要参观的城市需要走的点。
{
    vis[u]=1;
    for(int i=G.head[u];i!=-1;i=G.next[i])
    {
        int v=G.pnt[i];
        if(vis[v]) continue;
        dfs1(v);
        have[u]+=have[v];
    }
}
void dfs2(int u)
{
    vis[u]=1;
    for(int i=G.head[u];i!=-1;i=G.next[i])
    {
        int v=G.pnt[i];
        int w=G.val[i];
        if(vis[v] || have[v]==0) continue;
        len+=w; //len为所有边的和
        dfs2(v); 
        if(dis[u]<dis[v]+w) dis[u]=dis[v]+w;//距离m最远的距离。
    }
}
int main()
{
    //FF;
    int n,i,m;
    while(~scanf("%d%d",&n,&m))
    {
        G.init();mem(have,0);
        int k;
        for(i=1;i<n;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            G.add(a,b,c); G.add(b,a,c);
        }
        scanf("%d",&k);
        while(k--)
        {
            scanf("%d",&i);
            have[i]=1;
        }
        mem(vis,0);
        dfs1(m);
        mem(vis,0);
        mem(dis,0);
        dfs2(m);
        printf("%d\n",len*2-dis[m]);
    }
    return 0;
}

解法二:dp 1不回到 父节点几点,dp2回到。

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
#define FF  freopen("Input.txt","r",stdin)
#define mem(x,y) memset(x,y,sizeof(x))
#define ll long long
#define inf 1000020
const int N=50010;
int dp1[N],dp2[N];
struct Graph
{
    int head[N*2],val[N*2],pnt[N*2],next[N*2],tot;
    void init()
    {
        tot=0;
        mem(head,-1);
    }
    void add(int u,int v,int w)
    {
        pnt[tot]=v;
        val[tot]=w;
        next[tot]=head[u];
        head[u]=tot++;
    }
}G;
int tot,dis[N],vis[N],have[N],len;
void dfs1(int u)
{
    vis[u]=1;
    for(int i=G.head[u];i!=-1;i=G.next[i])
    {
        int v=G.pnt[i];
        if(vis[v]) continue;
        dfs1(v);
        have[u]+=have[v];
    }
}
void dfs2(int s)
{
    vis[s]=1;
    for(int i=G.head[s];i!=-1;i=G.next[i])
    {
        int t=G.pnt[i];
        int w=G.val[i];
        if(vis[t] || have[t]==0) continue;
        dfs2(t);
        dp1[s]=min(dp2[s]+dp1[t]+w,dp1[s]+dp2[t]+2*w);
        // 从s到t,不回到s,到s的t子树中,到s的其他子树中。
        dp2[s]+=dp2[t]+2*w;//s访问t的回到s。
    }
}
int main()
{
    //FF;
    int n,i,m;
    while(~scanf("%d%d",&n,&m))
    {
        G.init();mem(have,0);
        int k;
        for(i=1;i<n;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            G.add(a,b,c); G.add(b,a,c);
        }
        scanf("%d",&k);
        while(k--)
        {
            scanf("%d",&i);
            have[i]=1;
        }
        mem(vis,0);
        dfs1(m);
        mem(vis,0); mem(dp1,0);mem(dp2,0);
        dfs2(m);
        printf("%d\n",dp1[m]);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值