882. 细分图中的可到达结点

给你一个无向图(原始图),图中有 n 个节点,编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链,每条边之间的新节点数各不相同。

图用由边组成的二维数组 edges 表示,其中 edges[i] = [ui, vi, cnti] 表示原始图中节点 ui 和 vi 之间存在一条边,cnti 是将边 细分 后的新节点总数。注意,cnti == 0 表示边不可细分。

要 细分 边 [ui, vi] ,需要将其替换为 (cnti + 1) 条新边,和 cnti 个新节点。新节点为 x1, x2, ..., xcnti ,新边为 [ui, x1], [x1, x2], [x2, x3], ..., [xcnti+1, xcnti], [xcnti, vi] 。

现在得到一个 新的细分图 ,请你计算从节点 0 出发,可以到达多少个节点?如果节点间距离是 maxMoves 或更少,则视为 可以到达 。

给你原始图和 maxMoves ,返回 新的细分图中从节点 0 出发 可到达的节点数 。

示例 1:

输入:edges = [[0,1,10],[0,2,1],[1,2,2]], maxMoves = 6, n = 3
输出:13
解释:边的细分情况如上图所示。
可以到达的节点已经用黄色标注出来。

示例 2:

输入:edges = [[0,1,4],[1,2,6],[0,2,8],[1,3,1]], maxMoves = 10, n = 4
输出:23

示例 3:

输入:edges = [[1,2,4],[1,4,5],[1,3,1],[2,3,4],[3,4,5]], maxMoves = 17, n = 5
输出:1
解释:节点 0 与图的其余部分没有连通,所以只有节点 0 可以到达。

提示:

    0 <= edges.length <= min(n * (n - 1) / 2, 104)
    edges[i].length == 3
    0 <= ui < vi < n
    图中 不存在平行边
    0 <= cnti <= 104
    0 <= maxMoves <= 109
    1 <= n <= 3000

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法:Dijkstra 算法

思路

将原始图作为加权无向图处理,我们可以使用 Dijkstra 算法查找原始图中的所有可到达结点。 但是,这不足以解决仅部分使用细分边的示例。

当我们沿着边(沿任一方向)行进时,我们可以跟踪我们使用它的程度。最后,我们想知道我们在原始图中到达的每个结点,以及每条边的利用率之和。

算法

我们使用 Dijkstra 算法 来找出从源到所有目标的最短距离。 这是一个教科书算法, 请参阅此链接了解详细信息。

另外,对于每条(有向)边 (node,nei),我们将跟踪有多少新结点(从原始边细分而来的新结点)被使用。 最后,我们将总结每条边的利用率。

有关更多详细信息,请参阅内联注释。

作者:LeetCode
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/xi-fen-tu-zhong-de-ke-dao-da-jie-dian-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public int reachableNodes(int[][] edges, int M, int N) {
        Map> graph = new HashMap();
        for (int[] edge: edges) {
            int u = edge[0], v = edge[1], w = edge[2];
            graph.computeIfAbsent(u, x->new HashMap()).put(v, w);
            graph.computeIfAbsent(v, x->new HashMap()).put(u, w);
        }

        PriorityQueue pq = new PriorityQueue(
            (a, b) -> Integer.compare(a.dist, b.dist));
        pq.offer(new ANode(0, 0));

        Map dist = new HashMap();
        dist.put(0, 0);
        Map used = new HashMap();
        int ans = 0;

        while (!pq.isEmpty()) {
            ANode anode = pq.poll();
            int node = anode.node;
            int d = anode.dist;

            if (d > dist.getOrDefault(node, 0)) continue;
            // Each node is only visited once.  We've reached
            // a node in our original graph.
            ans++;

            if (!graph.containsKey(node)) continue;
            for (int nei: graph.get(node).keySet()) {
                // M - d is how much further we can walk from this node;
                // weight is how many new nodes there are on this edge.
                // v is the maximum utilization of this edge.
                int weight = graph.get(node).get(nei);
                int v = Math.min(weight, M - d);
                used.put(N * node + nei, v);

                // d2 is the total distance to reach 'nei' (nei***or) node
                // in the original graph.
                int d2 = d + weight + 1;
                if (d2 < dist.getOrDefault(nei, M+1)) {
                    pq.offer(new ANode(nei, d2));
                    dist.put(nei, d2);
                }
            }
        }

        // At the end, each edge (u, v, w) can be used with a maximum
        // of w new nodes: a max of used[u, v] nodes from one side,
        // and used[v, u] nodes from the other.
        // [We use the encoding (u, v) = u * N + v.]
        for (int[] edge: edges) {
            ans += Math.min(edge[2], used.getOrDefault(edge[0] * N + edge[1], 0) +
                                     used.getOrDefault(edge[1] * N + edge[0], 0) );
        }

        return ans;
    }
}

class ANode {
    int node, dist;
    ANode(int n, int d) {
        node = n;
        dist = d;
    }
}


作者:LeetCode
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/xi-fen-tu-zhong-de-ke-dao-da-jie-dian-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

先无视图中小点,按Dijkstra得出0到各大点的最短距离。然后考虑从每个大点出发,基于maxMove - distance[i]得出能到达几个小点。

    上图中我们称:大点(n个节点),小点(n个节点之间的新节点)
    那么小点个数+1,即大点间的距离。参考图中0->1,直线距离是11
    无负边无环,通过 Djistra + 限制条件maxMove 得出是否可达 reached[]+最小距离distances[]
    然后分别考虑

    大点:根据 是否可达,计数: if(reached[i]) ret++;
    小点:根据 最小距离,加上抛除maxMove剩余的距离还能达到多少,计数: if(reached[i]) ret+= maxMove-distances[i]。比如上图中的节点1,distances[1] = 5, 意思是0->1需要5步,那么总计不超过maxMove6的前提下,只能再走 maxMove-distances[i] 步
    计算小点时,一条边的两个大点分别出发,有可能重复计数,所以 ret+= Math.min(nodeCountBetweenIJ, fromI+fromJ)

    时间复杂度 O(ElogE + V), 空间复杂度 O(V)

作者:sodawy
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/javadijkstra-yi-bu-si-lu-bian-huan-by-so-lwfi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    int[] distances;
    boolean[] reached;
    public int reachableNodes(int[][] edges, int maxMoves, int n) {
        //init 
        distances = new int[n];
        reached = new boolean[n];
        
        //build graph
        Map> graph = buildGraph(edges, n);
        
        //djistra get shortest and reachable distances 
        djistra(graph, maxMoves);
        
        //count sub-divdedNodes from the nodes
        int ret = 0;
        for(int[] edge : edges) {
            int from = edge[0], to = edge[1], nodeCount = edge[2];
            int sum = 0;
            sum += reached[from] ? maxMoves - distances[from] : 0;
            sum += reached[to] ? maxMoves - distances[to] : 0;
            ret += Math.min(nodeCount, sum);
        }
        for(int i = 0; i < n; i++) {
            if(reached[i]) {
                ret++;
            }
        }
        
        return ret;
    }
    private void djistra(Map> graph, int maxMoves) {
        Arrays.fill(distances, Integer.MAX_VALUE);
        
        PriorityQueue pq = new PriorityQueue<>((a,b)-> a[0] - b[0]);
        pq.offer(new int[]{0, 0});
        distances[0] = 0;
        
        while(!pq.isEmpty()) {
            int[] node = pq.poll();
            int curD = node[0], from = node[1];
            if(reached[from]) continue;
            reached[from] = true;
            //firstTime we got `from`, curD is the shortest distance to `source`
            distances[from] = curD;
            
            Map nextMap = graph.get(from);
            for(int to : nextMap.keySet()) {
                if(reached[to]) continue;
                if(curD + nextMap.get(to) <= maxMoves) {
                    pq.offer(new int[]{curD + nextMap.get(to), to});    
                }
            }
        }
    }
    // from, to>>
    private Map> buildGraph(int[][] edges, int n) {
        Map> graph = new HashMap<>();
        for(int i = 0; i < n; i++) {
            graph.put(i, new HashMap<>());
        }
        for(int[] edge : edges) {
            int from = edge[0];
            int to = edge[1];
            int count = edge[2] + 1;
            graph.get(from).put(to, count);
            graph.get(to).put(from, count);
        }
        return graph;
    }
}


作者:sodawy
链接:https://leetcode.cn/problems/reachable-nodes-in-subdivided-graph/solution/javadijkstra-yi-bu-si-lu-bian-huan-by-so-lwfi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(java,算法,leetcode,职场和发展)