P12906 [NERC 2020] Guide 题解

题目链接:https://www.luogu.com.cn/problem/P12906

题目概述

给定一棵有根树(根节点为1),需要找到一条从根节点出发的最短路径,使得路径访问恰好k个不同的节点。路径可以重复访问节点,但只计算第一次访问时的节点。

解题思路

关键观察

  1. 路径结构分析:最优路径通常由两部分组成:

    • 沿着最长链(从根节点到最深叶节点的路径)访问节点

    • 访问一些分支节点,每个分支节点需要额外的往返边

  2. 路径长度公式

    • 如果所有k个节点都在最长链上:路径长度 = k - 1

    • 如果需要访问分支节点:路径长度 = 2(k - L) + (L - 1),其中L是最长链长度

  3. 路径构造策略

    • 优先访问分支节点(需要往返)

    • 最后访问最长链上的节点(单向访问)

算法实现

数据结构

  • 使用邻接表存储树结构

  • 记录每个节点的深度

  • 标记最长链上的节点

  • 记录已访问的节点

核心步骤

  1. 深度计算:通过DFS计算每个节点的深度,并找到最大深度

  2. 标记最长链:从深度最大的节点回溯到根节点,标记路径上的节点

  3. 路径构造:通过DFS构造路径,先访问分支节点后访问最长链节点

代码实现

复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int N=1e2+7;
vector<int>g[N];
int dep[N],sum,mx;
int n,k;
bool vis[N],vis1[N];

// 计算节点深度和最大深度
void dfs(int u,int fa){
    for(auto v:g[u]){
        if(v==fa)continue;
        dep[v]=dep[u]+1;
        mx=max(mx,dep[v]);
        dfs(v,u);    
    }
}

// 标记最长链上的节点
bool dfs1(int u,int fa){
    if(dep[u]==mx){
        vis[u]=1;
        return 1;    
    }
    for(auto v:g[u]){
        if(v==fa)continue;    
        if(dfs1(v,u)){
            vis[u]=1;
            return 1;
        }
    }
    return 0;    
}

// 构造并输出路径
void dfs2(int u,int fa){
    cout<<u<<" ";
    if(!vis[u]&&!vis1[u]){
        sum++;
        vis1[u]=1;    
    }
    for(auto v:g[u]){
        if(sum-1>=k-mx)break;  // 分支节点数达到要求时停止
        if(v==fa)continue;
        if(vis[v])continue;    // 跳过最长链节点
        dfs2(v,u);    
        cout<<u<<" ";          // 回溯
    }
    for(auto v:g[u]){
        if(vis[v]){
            dfs2(v,u);         // 访问最长链节点
            break;
        }    
    }    
}

signed main(){
    int t;cin>>t;
    while(t--){
        cin>>n>>k;
        // 初始化
        for(int i=1;i<=n;i++){
            g[i].clear();
            dep[i]=0;
            vis[i]=0;
            vis1[i]=0;
        }
        // 建树
        for(int i=2;i<=n;i++){
            int u;cin>>u;
            g[u].push_back(i);
        }
        // 计算深度和最长链
        dep[1]=1;    
        sum=1;
        mx=1;  // 关键:初始化mx=1,处理只有根节点的情况
        dfs(1,0);
        
        // 计算路径长度
        int ans=0;
        if(mx>=k)ans=k-1;
        else ans=2*(k-mx)+mx-1;
        cout<<ans<<endl;
        
        // 调整mx,确保不超过k
        mx=min(mx,k);
        // 标记最长链并输出路径
        dfs1(1,0);
        dfs2(1,0);
        cout<<endl;
    }
}
相关推荐
NAGNIP9 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
颜酱17 小时前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub20 小时前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub20 小时前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub20 小时前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub21 小时前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub21 小时前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉
NAGNIP1 天前
轻松搞懂全连接神经网络结构!
人工智能·算法·面试
NAGNIP1 天前
一文搞懂激活函数!
算法·面试
董董灿是个攻城狮1 天前
AI 视觉连载7:传统 CV 之高斯滤波实战
算法