算法初探LeetCode-从第一个节点出发到最后一个节点的受限路径数

227 阅读1分钟

LeetCode1786:从第一个节点出发到最后一个节点的受限路径数

现有一个加权无向连通图。给你一个正整数 n ,表示图中有 n 个节点,并按从 1 到 n 给节点编号;另给你一个数组 edges ,其中每个 edges[i] = [ui, vi, weighti] 表示存在一条位于节点 ui 和 vi 之间的边,这条边的权重为 weighti 。

从节点 start 出发到节点 end 的路径是一个形如 [z0, z1, z2, ..., zk] 的节点序列,满足 z0 = start 、zk = end 且在所有符合 0 <= i <= k-1 的节点 zi 和 zi+1 之间存在一条边。

路径的距离定义为这条路径上所有边的权重总和。用 distanceToLastNode(x) 表示节点 n 和 x 之间路径的最短距离。受限路径 为满足 distanceToLastNode(zi) > distanceToLastNode(zi+1) 的一条路径,其中 0 <= i <= k-1 。

返回从节点 1 出发到节点 n 的 受限路径数 。由于数字可能很大,请返回对 109 + 7 取余 的结果。

示例 1:

输入: n = 5, edges = [[1,2,3],[1,3,3],[2,3,1],[1,4,2],[5,2,2],[3,5,1],[5,4,10]]
输出: 3
解释: 每个圆包含黑色的节点编号和蓝色的 distanceToLastNode 值。三条受限路径分别是:
1) 1 --> 2 --> 5
2) 1 --> 2 --> 3 --> 5
3) 1 --> 3 --> 5

示例 2:

输入: n = 7, edges = [[1,3,1],[4,1,2],[7,3,4],[2,5,3],[5,6,1],[6,7,2],[7,5,3],[2,6,4]]
输出: 1
解释: 每个圆包含黑色的节点编号和蓝色的 distanceToLastNode 值。唯一一条受限路径是:1 --> 3 --> 7

提示:

  • 1<=n<=21041 <= n <= 2 * 10^4
  • n1<=edges.length<=4104n - 1 <= edges.length <= 4 * 10^4
  • edges[i].length == 3
  • 1 <= ui, vi <= n
  • ui != vi
  • 1<=weighti<=1051 <= weighti <= 10^5
  • 任意两个节点之间至多存在一条边
  • 任意两个节点之间至少存在一条路径

思路分析

首先通过最短路径(dijkstra)算法计算 n 到其他节点的最短路径,本题需要使用优先队列 + 邻接表的形式计算最短路径否则内存将会超出限制,然后基于限制条件进行有向无环图(DAG)的重新构建,最后通过拓扑排序以及 dp 计算出路径数

求出每个节点到节点n的距离。然后使用记忆化搜索求出每个节点到节点n的条数,其中条件就用求出的距离。

算法代码

long[] d;
List < int[] > [] g;
int MOD = (int)(1e9 + 7);
int[] cnt;

public int countRestrictedPaths(int n, int[][] edges) {
    d = new long[n + 1];
    g = new List[n + 1];
    cnt = new int[n + 1];
    for (int i = 1; i <= n; i++) {
        g[i] = new ArrayList < > ();
    }
    for (int[] x: edges) {
        g[x[0]].add(new int[] {
            x[1], x[2]
        });
        g[x[1]].add(new int[] {
            x[0], x[2]
        });
    }
    Arrays.fill(d, Long.MAX_VALUE / 2);
    Arrays.fill(cnt, -1);
    PriorityQueue < long[] > q = new PriorityQueue < > ((a, b) - > Long.compare(a[1], b[1]));
    q.offer(new long[] {
        n, 0
    });
    while (q.size() > 0) {
        long[] p = q.poll();
        if (p[1] >= d[(int) p[0]]) {
            continue;
        }
        d[(int) p[0]] = p[1];
        for (int[] ne: g[(int) p[0]]) {
            if (d[ne[0]] == Long.MAX_VALUE / 2) {
                q.offer(new long[] {
                    ne[0], p[1] + ne[1]
                });
            }
        }
    }
    return dfs(n);
}

public int dfs(int u) {
    if (cnt[u] != -1) {
        return cnt[u];
    }
    if (u == 1) {
        return 1;
    }
    int res = 0;
    for (int[] x: g[u]) {
        if (d[x[0]] > d[u]) {
            res = (res + dfs(x[0])) % MOD;
        }
    }
    cnt[u] = res;
    return res;
}

结果详情

nxy1.png

算法复杂度

  • 空间复杂度:O(1)O(1)
  • 时间复杂度:O(n)O(n)

掘金(JUEJIN)一起进步,一起成长!