树的性质:利用树的唯一路径特性,避免复杂的最短路径计算。

定理:一定先完成同一子树下目标点的遍历,故需要记录父节点遍历次序,通过逆序bfs来实现自下而上,通过父节点记录来累加得到父节点的累计值(因为记录了父节点故有严格的两边关系,不需要按照dfs的后序来实现子树下的累加)

  1. 分步骤拆解
    建树:用邻接表存储树结构。

BFS预处理:计算父节点和拓扑序,为子树统计做准备。

统计任务点:初始化每个节点的cntS(取货点)和cntT(送货点)。

子树汇总:逆序处理拓扑序,自底向上累加子树的任务点数量。

计算总路程:遍历所有边,根据cntS和cntT判断是否经过,累加边权。

例子:6.11华为.1 https://niumacode.com/training/131/problem/P1687

include

include

include

using namespace std;

int main() {
// 输入处理
int N, M;
cin >> N >> M;
vector<vector<pair<int, int>>> adj(N + 1);
for (int i = 0; i < N - 1; ++i) {
int u, v, c;
cin >> u >> v >> c;
adj[u].push_back({v, c});
adj[v].push_back({u, c});
}

// BFS建立父节点和拓扑序
vector fa(N + 1, 0), order;
queue q;
q.push(1);
fa[1] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
order.push_back(u);
for (auto [v, c] : adj[u]) {
if (v != fa[u]) {
fa[v] = u;
q.push(v);
}
}
}

// 初始化任务点统计
vector cntS(N + 1, 0), cntT(N + 1, 0);
for (int i = 0; i < M; ++i) {
int s, t;
cin >> s >> t;
cntS[s]++;
cntT[t]++;
}

// 逆序汇总子树
for (int i = order.size() - 1; i >= 0; --i) {
int u = order[i];
if (fa[u] != 0) {
cntS[fa[u]] += cntS[u];
cntT[fa[u]] += cntT[u];
}
}

// 计算总路程
long long sumS = 0, sumT = 0;
for (int u = 2; u <= N; ++u) {
int p = fa[u];
for (auto [v, c] : adj[u]) {
if (v == p) {
if (cntS[u] > 0) sumS += c;
if (cntT[u] > 0) sumT += c;
break;
}
}
}
cout << 2 * (sumS + sumT) << endl;
return 0;
}

第二题同样是树结构,但是主要和深度、叶子节点有关,故需要储存的是children数组

  • 输入处理:读取设备数量n和连接关系,构建邻接表adj。

  • DFS遍历:计算每个节点的深度depth和子节点列表children,同时记录最大深度max_depth。

  • 动态规划求解:
    对每个可能的深度h(从0到max_depth):按深度分层存储节点by_depth。
    初始化best数组,best[v]表示以v为根的子树在目标深度h时的最优解。
    从最大深度开始反向计算:如果节点深度大于h,设为极小值。
    如果节点深度等于h,设为1(保留该节点)。
    否则,累加子节点的有效解,并更新当前节点的best值。
    记录根节点的最优解answer。
    输出结果:需要移除的设备数为n - answer。

    include

    include

    include

    using namespace std;

    vector<vector> adj;
    vector depth;
    vector<vector> children;
    int max_depth = 0;

    void dfs(int u, int p) {
    for (int v : adj[u]) {
    if (v == p) continue;
    depth[v] = depth[u] + 1;
    max_depth = max(max_depth, depth[v]);
    children[u].push_back(v);
    dfs(v, u);
    }
    }

    int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    adj.resize(n + 1);
    depth.resize(n + 1);
    children.resize(n + 1);

    for (int i = 0; i < n - 1; ++i) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
    }

    dfs(1, 0);

    int answer = 0;
    for (int h = 0; h <= max_depth; ++h) {
    vector<vector> by_depth(max_depth + 1);
    for (int v = 1; v <= n; ++v) {
    by_depth[depth[v]].push_back(v);
    }

    vector best(n + 1, -1e9);
    for (int d = max_depth; d >= 0; --d) {
    for (int v : by_depth[d]) {
    if (depth[v] > h) {
    best[v] = -1e9;
    } else if (depth[v] == h) {
    best[v] = 1;
    } else {
    int s = 0;
    for (int u : children[v]) {
    if (best[u] > 0) {
    s += best[u];
    }
    }
    best[v] = (s > 0) ? (s + 1) : -1e9;
    }
    }
    }
    answer = max(answer, best[1]);
    }

    cout << n - answer << endl;

    return 0;
    }