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;
    }
}
相关推荐
yi0116 小时前
LeetCode 219:存在重复元素 II——哈希表记录“最近一次出现的位置”
数据结构·人工智能·笔记·python·算法·leetcode·哈希表
灯澜忆梦8 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
小蒋观天下9 小时前
专项方案:大场景港口AI安防、多干扰环境下的算法调优与落地实操
人工智能·深度学习·算法·安全·机器学习·计算机视觉·ai大模型
Evand J9 小时前
【电机滤波例程5】渐消因子自适应扩展卡尔曼滤波(EKF),MATLAB。PMSM负载突变下的状态估计。附下载链接
算法·matlab
H.莓飛9 小时前
【数据结构】二叉树_OJ题
linux·开发语言·数据结构·算法
好想像大佬一样能够ak所有10 小时前
Leetcode35.搜索插入位置
数据结构·算法·leetcode
aqiu11111110 小时前
【C++算法打怪专栏】LeetCode 165. 比较版本号(双指针与字符串流法)
c++·算法·leetcode
黄金龙PLUS10 小时前
如何基于置换算法来设计对称加密算法
算法·网络安全·密码学·哈希算法·同态加密
不会就选b10 小时前
算法日常・每日刷题--<动态规划>14
算法
赛博守夜人10 小时前
跨境业务与出海合规之九:海外买量投放与营销虚假流量(Ad Fraud):跨国机房群控与归因欺诈识别算法
算法·安全