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;
    }
}
相关推荐
Dillon Dong3 小时前
【风电控制】TI TMS320F28379D 双CPU架构解析与任务分布设计
嵌入式硬件·算法·变流器·风电控制
小羊在睡觉8 小时前
力扣84. 柱状图中最大的矩形
后端·算法·leetcode·golang·go
3DVisionary8 小时前
蓝光三维扫描:医疗制造的精度焦虑怎么解
人工智能·算法·制造·蓝光三维扫描·医疗制造·三维检测·义齿检测
好评笔记9 小时前
机器学习面试八股——常用损失函数
人工智能·深度学习·算法·机器学习·校招
weixin_468466859 小时前
全局与局部注意力机制新手实战指南
人工智能·python·深度学习·算法·自然语言处理·transformer·注意力机制
_日拱一卒9 小时前
LeetCode:994腐烂的橘子
java·数据结构·算法·leetcode·深度优先
珂朵莉MM9 小时前
第七届全球校园人工智能算法精英大赛-算法巅峰赛产业命题赛第3赛季优化题--束搜索
人工智能·算法
Omics Pro10 小时前
首个!外源天然产物综合性代谢图谱
数据库·人工智能·算法·机器学习·r语言
voidmort10 小时前
3. 微调(Fine-tuning)与强化学习(RL)的核心思想
python·深度学习·算法
人道领域11 小时前
【LeetCode刷题日记】669.修剪二叉搜索树
开发语言·python·算法