题解:洛谷 P1099 [NOIP 2007 提高组] 树网的核

【题目来源】

洛谷:[P1099 NOIP 2007 提高组] 树网的核 - 洛谷

【题目描述】

\(T\)\(=\)\((V,E,W)\) 是一个无圈且连通的无向图(也称为无根树),每条边都有正整数的权,我们称 \(T\) 为树网(treenetwork),其中 \(V\)\(E\) 分别表示结点与边的集合,\(W\) 表示各边长度的集合,并设 \(T\)\(n\) 个结点。

路径:树网中任何两结点 \(a\)\(b\) 都存在唯一的一条简单路径,用 \(d(a,b)\) 表示以 \(a,b\) 为端点的路径的长度,它是该路径上各边长度之和。我们称 \(d(a,b)\)\(a,b\) 两结点间的距离。

\(D(v,P)=min\{d(v,u)\}\), \(u\) 为路径 \(P\) 上的结点。

树网的直径:树网中最长的路径称为树网的直径。对于给定的树网 T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。

偏心距 \(ECC(F)\):树网 \(T\) 中距路径 \(F\) 最远的结点到路径 \(F\) 的距离,即

\(ECC(F)=max\{D(v,F),v∈V\}\)

任务:对于给定的树网 \(T=(V,E,W)\) 和非负整数 \(s\),求一个路径 \(F\),他是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过 \(s\)(可以等于 \(s\)),使偏心距 \(ECC(F)\) 最小。我们称这个路径为树网 \(T=(V,E,W)\) 的核(Core)。必要时,\(F\) 可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。

下面的图给出了树网的一个实例。图中,\(A−B\)\(A−C\) 是两条直径,长度均为 \(20\)。点 \(W\) 是树网的中心,\(EF\) 边的长度为 \(5\)。如果指定 \(s=11\),则树网的核为路径DEFG(也可以取为路径DEF),偏心距为 \(8\)。如果指定 \(s=0\)(或 \(s=1\)\(s=2\)),则树网的核为结点 \(F\),偏心距为 \(12\)

image

【输入】

\(n\) 行。

\(1\) 行,两个正整数 \(n\)\(s\),中间用一个空格隔开。其中 \(n\) 为树网结点的个数,\(s\) 为树网的核的长度的上界。设结点编号以此为 \(1,2…,n\)

从第 \(2\) 行到第 \(n\) 行,每行给出 \(3\) 个用空格隔开的正整数 \(u,v,w\),依次表示每一条边的两个端点编号和长度。例如,2 4 7 表示连接结点 \(2\)\(4\) 的边的长度为 \(7\)

【输出】

一个非负整数,为指定意义下的最小偏心距。

【输入样例】

5 2
1 2 5
2 3 2
2 4 4
2 5 3

【输出样例】

5

【算法标签】

《洛谷 P1099 树网的核》 #模拟# #动态规划DP# #树形数据结构# #枚举# #最短路# #NOIP提高组# #2007#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 305;  // 最大节点数

int n;              // 节点数量
int s;              // 路径长度限制
int u, v, w;        // 临时变量:起点、终点、权重
int vis[N];         // 访问标记数组
int st, ed;         // 起点和终点
int dist[N];        // 距离数组
int maxx;           // 最大距离
int router[N];      // 存储最长路径
int router_t[N];    // 临时存储路径
int len, len_t;     // 路径长度
int ans;            // 最终答案
int color[N];       // 标记是否在核心路径上

vector<int> ve[N];  // 邻接表存储连接关系
vector<int> wi[N];  // 邻接表存储权重

// 第一次DFS:找到树的直径的一个端点
void dfs_1(int x, int y)
{
    // 更新最长路径
    if (y > maxx)
    {
        len = len_t;
        memcpy(router, router_t, sizeof(router)); // 复制路径
        maxx = y;
    }

    // 遍历所有邻接节点
    for (int i = 0; i < ve[x].size(); i++)
    {
        int tmp_v = ve[x][i];
        if (vis[tmp_v])
            continue;
        
        // 更新距离和路径
        dist[tmp_v] = y + wi[x][i];
        vis[tmp_v] = 1;
        len_t++;
        router_t[len_t] = tmp_v;
        
        // 递归搜索
        dfs_1(tmp_v, y + wi[x][i]);
        
        // 回溯
        vis[tmp_v] = 0;
        len_t--;
    }
}

// 第二次DFS:计算从核心路径出发的最远距离
void dfs_2(int x)
{
    // 更新最大距离
    ans = max(ans, dist[x]);
    
    // 遍历所有邻接节点
    for (int i = 0; i < ve[x].size(); i++)
    {
        int tmp_v = ve[x][i];
        if (vis[tmp_v] || color[tmp_v])
            continue;
        
        // 更新距离并继续搜索
        vis[tmp_v] = 1;
        dist[tmp_v] = dist[x] + wi[x][i];
        dfs_2(tmp_v);
        
        // 回溯
        vis[tmp_v] = 0;
    }
    return;
}

int main()
{
    // 输入节点数和路径长度限制
    cin >> n >> s;
    
    // 构建邻接表
    for (int i = 1; i < n; i++)
    {
        cin >> u >> v >> w;
        ve[u].push_back(v);
        wi[u].push_back(w);
        ve[v].push_back(u);
        wi[v].push_back(w);
    }

    // 第一次DFS:找到直径的一个端点
    st = 1;
    vis[st] = 1;
    router_t[++len_t] = st;
    dfs_1(st, 0);
    
    // 第二次DFS:找到直径的另一个端点
    st = router[len];
    memset(vis, 0, sizeof(vis));
    memset(dist, 0, sizeof(dist));
    memset(router, 0, sizeof(router));
    memset(router_t, 0, sizeof(router_t));
    len = len_t = 0;
    len_t = 1;
    router_t[len_t] = st;
    maxx = 0;
    vis[st] = 1;
    dfs_1(st, 0);
    ed = router[len];

    // 寻找最优的核心路径
    ans = 1e9;
    for (int i = 1; i <= len; i++)
    {
        for (int j = i; j <= len; j++)
        {
            if (dist[router[j]] - dist[router[i]] > s)
                break;
            
            // 计算当前路径的最大偏心距
            int t = max(dist[router[i]], dist[router[len]] - dist[router[j]]);
            if (t < ans)
            {
                ans = t;
                st = i;
                ed = j;
            }
        }
    }

    // 标记核心路径上的节点
    for (int i = st; i <= ed; i++)
        color[router[i]] = 1;

    // 从核心路径上的每个节点出发,计算最远距离
    for (int i = st; i <= ed; i++)
    {
        memset(vis, 0, sizeof(vis));
        memset(dist, 0, sizeof(dist));
        vis[router[i]] = 1;
        dfs_2(router[i]);
    }

    // 输出最小偏心距
    cout << ans << endl;
    return 0;
}

【运行结果】

8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
5
posted @ 2026-02-19 15:59  团爸讲算法  阅读(4)  评论(0)    收藏  举报