hihocoder 1035 自驾旅行 III(树形dp)

本文详细介绍了一道树形动态规划题目,通过分析不同出行方式(步行或驾车)及其返回状态,实现对树状结构中特定节点的最优路径求解。文章提供了完整的代码示例,并解释了关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本蒟蒻一道树形dp做了一天,OrzOrzOrz.....菜得难以启齿~~

题解的话这篇博客说得听清楚的~~~

附上大佬的链接:点击打开链接

同时附上本苣菜的代码吧~~

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL inf=0x3f3f3f3f3f3f3f3f;
const int MAXN=1000100;
struct edge_t
{
    int v,next;
    LL c0,c1;
}edge[MAXN*2];
int head[MAXN];
int tot;
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,LL c0,LL c1)
{
    edge[tot].v=v;
    edge[tot].c0=c0;
    edge[tot].c1=c1;
    edge[tot].next=head[u];
    head[u]=tot++;
}
//每到一个节点,分情况讨论
//首先没车:
//      分为两种情况:
//      (1).步行出门且回来
//      (2).步行出门人不回来
//如果有车
//      分为三种情况:
//      (1).可以开车出门(开车次数>=0),且如果骑车,人车都回来,否则人必须回来
//      (2).可以开车出门(开车次数>=1),车可以不用回来,但人必须回来
//      (3).可以开车出门(开车次数>=1),车可以没有回来,人也可以没有回来
#define on_foot_ret 0
#define on_foot_noret 1
#define car_people 2
#define nocar_people 3
#define nocar_nopeople 4
bool imp[MAXN];
LL dp[MAXN][5];
void debug(int u)
{
    printf("Vertex :%d\n",u);
    printf("On_foot_ret :%lld\n",dp[u][0]);
    printf("On_foot_noret :%lld\n",dp[u][1]);
    printf("Car_people :%lld\n",dp[u][2]);
    printf("Nocar_people :%lld\n",dp[u][3]);
    printf("Nocar_Nopeople :%lld\n",dp[u][4]);
    putchar('\n');
}
bool useful[MAXN];
void dfs(int u,int fa)
{
    useful[u]=imp[u]?true:false;
    LL chajia1=0;
    LL chajia2=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==fa) continue;
        LL c0=edge[i].c0;
        LL c1=edge[i].c1;
        dfs(v,u);
        if(useful[v])
        {
            useful[u]=true;
            dp[u][on_foot_ret]+=(dp[v][on_foot_ret]+2*c0);
            chajia1=max(chajia1,dp[v][on_foot_ret]+c0-dp[v][on_foot_noret]);
            dp[u][car_people]+=min(dp[v][on_foot_ret]+2*c0,dp[v][car_people]+2*c1);
            chajia2=max(chajia2,min(dp[v][on_foot_ret]+2*c0,dp[v][car_people]+2*c1)-(dp[v][nocar_people]+c0+c1));
        }
    }
    dp[u][on_foot_noret]=dp[u][on_foot_ret]-chajia1;
    dp[u][nocar_people]=dp[u][car_people]-chajia2;
    //ttf1表示选择nocar_people,ttf2表示on_foot_noret(遍历最后一个子树的时候没车了)
    //same表示遍历最后一棵子树的时候还有车
    LL ttf1=0,ttf2=0,same=0;
    int match1=-1,match2=-1;
    LL prettf1=0,prettf2=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==fa) continue;
        LL c0=edge[i].c0;
        LL c1=edge[i].c1;
        if(useful[v])
        {
            LL tmp1=min(dp[v][on_foot_ret]+2*c0,dp[v][car_people]+2*c1)-(dp[v][nocar_people]+c0+c1);
            LL tmp2=min(dp[v][on_foot_ret]+2*c0,dp[v][car_people]+2*c1)-(dp[v][on_foot_noret]+c0);
            LL tmp3=min(dp[v][on_foot_ret]+2*c0,dp[v][car_people]+2*c1)-min(dp[v][on_foot_noret]+c0,dp[v][nocar_nopeople]+c1);
            //ttf1和ttf2不能属于同一棵子树
            if(tmp1>=ttf1)
            {
                match1=v;
                prettf1=ttf1;
                ttf1=tmp1;
            }
            else if(tmp1>prettf1) prettf1=tmp1;

            if(tmp2>=ttf2)
            {
                match2=v;
                prettf2=ttf2;
                ttf2=tmp2;
            }
            else if(tmp2>prettf2) prettf2=tmp2;

            same=max(same,tmp3);
        }
    }
    LL temp;

    if(match1!=match2) temp=ttf1+ttf2;
    else temp=max(ttf1+prettf2,ttf2+prettf1);

    dp[u][nocar_nopeople]=dp[u][car_people]-max(temp,same);

//    if(useful[u]) debug(u);

}
int main()
{
//    freopen("in.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        memset(imp,false,sizeof(imp));
        memset(useful,false,sizeof(useful));
        init();
        for(int i=1;i<n;i++)
        {
            int u,v;
            LL c0,c1;
            scanf("%d%d%lld%lld",&u,&v,&c0,&c1);
            addedge(u,v,c0,c1);
            addedge(v,u,c0,c1);
        }
        int w;
        scanf("%d",&w);
        for(int i=1;i<=w;i++)
        {
            int flag;
            scanf("%d",&flag);
            imp[flag]=true;
        }
        int root=1;
        dfs(root,-1);
        printf("%lld\n",min(dp[root][on_foot_noret],dp[root][nocar_nopeople]));
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值