You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求构成权重和为L的简单路径所需的最少边数

带权树中寻找权重和为L的最少边数简单路径的高效解法

问题定义

给定带权树T,需找到权重和恰好为L的简单路径(无重复顶点/边),返回该路径所需的最少边数;若不存在这样的路径,返回-1。补充说明:每个测试用例的L可能不同,树包含N个顶点,编号0到N-1。

现有解法的瓶颈

你当前的暴力解法通过遍历所有N²条路径来寻找答案,对应的Java代码可正常运行,但当N达到10000时会因时间复杂度O(N²)超时。尝试过二分答案,但验证某一边数是否可行的难度与原问题相当,无法有效优化。

高效解法思路

树中的任意简单路径只有两种形式:

  1. 从某个节点出发向下延伸的单链路径;
  2. 经过某个公共祖先的两条单链路径组合(路径两端在该祖先的不同子树中)。

我们可以通过DFS遍历+哈希表维护前缀和的方式,将时间复杂度优化到O(N log N),具体步骤如下:

  • 遍历树时,记录从根到当前节点的路径权重和currentSum,以及对应的边数currentCnt;
  • 用哈希表存储已访问路径的前缀和,以及该前缀和对应的最小边数(同一前缀和对应多个边数时,保留最小的才能得到最优解);
  • 对每个节点,检查是否存在前缀和needed = currentSum - L:若存在,则路径从对应节点到当前节点的权重和为L,边数为currentCnt - sumMap.get(needed),更新全局最小边数;
  • 回溯时移除当前节点的前缀和记录,避免统计同一子树内的重复路径。

若树中所有边权重均为正,还可以用排序前缀和+双指针的方式进一步优化,但哈希表的方法更通用(支持负权重)。

优化后的Java代码

import java.util.*;

class Edge {
    int toVertex, weight;
    Edge(int v, int w) {
        toVertex = v;
        weight = w;
    }
}

class Solver {
    private static long targetL;
    private static int minEdges;
    private static Map<Long, Integer> sumToMinCnt;

    public static int solve(List<List<Edge>> tree, long L) {
        targetL = L;
        minEdges = Integer.MAX_VALUE;
        sumToMinCnt = new HashMap<>();
        // 初始状态:虚拟根的前缀和为0,边数为0
        sumToMinCnt.put(0L, 0);
        dfs(tree, 0, -1, 0L, 0);
        return minEdges == Integer.MAX_VALUE ? -1 : minEdges;
    }

    private static void dfs(List<List<Edge>> tree, int currNode, int parent, long currSum, int currEdgeCnt) {
        // 检查是否存在前缀和使得currSum - sum = targetL
        long requiredSum = currSum - targetL;
        if (sumToMinCnt.containsKey(requiredSum)) {
            minEdges = Math.min(minEdges, currEdgeCnt - sumToMinCnt.get(requiredSum));
        }

        // 记录当前前缀和的最小边数:如果已有相同sum,保留更小的边数
        sumToMinCnt.put(currSum, Math.min(
                sumToMinCnt.getOrDefault(currSum, Integer.MAX_VALUE),
                currEdgeCnt
        ));

        // 遍历子节点
        for (Edge edge : tree.get(currNode)) {
            int nextNode = edge.toVertex;
            if (nextNode == parent) continue;
            dfs(tree, nextNode, currNode, currSum + edge.weight, currEdgeCnt + 1);
        }

        // 回溯:如果当前前缀和的边数是我们记录的最小值,就移除该条目,避免影响其他子树
        if (sumToMinCnt.get(currSum) == currEdgeCnt) {
            sumToMinCnt.remove(currSum);
        }
    }
}

public class Main {
    static void putEdge(List<List<Edge>> tree, int v1, int v2, int weight) {
        tree.get(v1).add(new Edge(v2, weight));
        tree.get(v2).add(new Edge(v1, weight));
    }

    public static void main(String[] args) {
        List<List<Edge>> tree = new ArrayList<>();
        int N = 8;
        for (int i = 0; i < N; i++) tree.add(new ArrayList<>());
        putEdge(tree, 0, 1, 2);
        putEdge(tree, 1, 2, 1);
        putEdge(tree, 1, 3, 2);
        putEdge(tree, 2, 6, 1);
        putEdge(tree, 6, 7, 1);
        putEdge(tree, 3, 4, 1);
        putEdge(tree, 3, 5, 4);
        System.out.println(Solver.solve(tree, 5L)); // 输出2,对应路径4->3->5
    }
}

代码说明

  • 全局变量sumToMinCnt存储前缀和到最小边数的映射,确保同一前缀和只保留能生成最短路径的边数;
  • DFS遍历中,每次处理节点时先检查是否存在符合条件的前缀和,再将当前前缀和加入映射;
  • 回溯时移除当前节点的前缀和,保证不同子树的路径不会被重复计算;
  • 时间复杂度为O(N log N),哈希表的平均操作时间为O(1),最坏情况(如哈希冲突)为O(log N),完全适配N=10000的规模。

内容的提问来源于stack exchange,提问作者user25680598

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 13:55:57