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;
    }
}
相关推荐
wuminyu22 分钟前
JUC组件逐层剥离与深度剖析
java·linux·c语言·jvm·c++·算法
hhzz1 小时前
机器学习-算法模型系列文章:07-SVM 一巴掌拍出来的超平面:SVM核函数选错,再干净的数据也救不了你
算法·机器学习·支持向量机
geovindu1 小时前
java: Gale-Shapley Algorithm
java·开发语言·后端·算法
冻柠檬飞冰走茶1 小时前
PTA基础编程题目集 7-34 通讯录的录入与显示(C语言实现)
c语言·开发语言·数据结构·算法
zander2581 小时前
LeetCode 79. 单词搜索
算法·深度优先
老洋葱Mr_Onion2 小时前
【C++】高精度模板
开发语言·c++·算法
脚踏实地皮皮晨2 小时前
003003002_WPF Grid 基类官方类定义逐行深度解析
开发语言·windows·算法·c#·wpf·visual studio
aaaameliaaa10 小时前
字符函数和字符串函数
c语言·笔记·算法
城管不管11 小时前
ReAct、Plan-and-Execute、Reflection 三大智能 Agent 范式核心区别
java·人工智能·算法·spring·ai·动态规划
月疯12 小时前
二分法算法(水平等分图形面积)
算法