Hard

题目描述

给你一个无向加权连通图,包含 n 个标记为 0n - 1 的节点,以及一个整数数组 edges,其中 edges[i] = [ai, bi, wi] 表示节点 aibi 之间有一条权重为 wi 的边。

某些边的权重为 -1wi = -1),而其他边具有正权重(wi > 0)。

你的任务是修改所有权重为 -1 的边,为它们分配 [1, 2 * 109] 范围内的正整数值,使得节点 sourcedestination 之间的最短距离等于整数 target。如果有多种修改方式使得 sourcedestination 之间的最短距离等于 target,任何一种都被认为是正确的。

如果可能使从 sourcedestination 的最短距离等于 target,则返回包含所有边(即使是未修改的边)的数组,顺序任意;如果不可能,则返回空数组。

注意:不允许修改初始权重为正数的边的权重。

示例 1:

输入:n = 5, edges = [[4,1,-1],[2,0,-1],[0,3,-1],[4,3,-1]], source = 0, destination = 1, target = 5
输出:[[4,1,1],[2,0,1],[0,3,3],[4,3,1]]
解释:上图显示了对边的可能修改,使从 0 到 1 的距离等于 5。

示例 2:

输入:n = 3, edges = [[0,1,-1],[0,2,5]], source = 0, destination = 2, target = 6
输出:[]
解释:上图包含初始边。通过修改权重为 -1 的边,无法使从 0 到 2 的距离等于 6。因此返回空数组。

示例 3:

输入:n = 4, edges = [[1,0,4],[1,2,3],[2,3,5],[0,3,-1]], source = 0, destination = 2, target = 6
输出:[[1,0,4],[1,2,3],[2,3,5],[0,3,1]]
解释:上图显示了修改后的图,从 0 到 2 的最短距离为 6。

约束条件:

  • 1 <= n <= 100
  • 1 <= edges.length <= n * (n - 1) / 2
  • edges[i].length == 3
  • 0 <= ai, bi < n
  • wi = -11 <= wi <= 107
  • ai != bi
  • 0 <= source, destination < n
  • source != destination
  • 1 <= target <= 109
  • 图是连通的,没有自环或重复边

解题思路

这是一道复杂的图论题,需要通过修改权重为-1的边来使最短路径长度等于目标值。解题思路如下:

核心思路:

  1. 首先验证可行性:计算只使用正权重边的最短路径,如果已经小于target则不可能
  2. 将所有-1边临时设为1,计算最短路径,如果大于target则不可能
  3. 使用二分搜索的思想:逐步调整-1边的权重,使最短路径接近target

具体步骤:

  1. 先将所有-1边设为大值(如2×10⁹),计算最短路径,验证基本连通性
  2. 将所有-1边设为1,计算最短路径,如果大于target则无解
  3. 逐个处理-1边:对每条-1边,尝试通过调整其权重使最短路径等于target
  4. 关键在于找到合适的边进行调整:如果存在边(u,v)使得dist[u] + dist[v] + 1 < target,则可以将该边权重设为target - dist[u] - dist[v]

算法优化:

  • 使用Dijkstra算法计算最短路径
  • 采用贪心策略:优先调整能够直接达到目标的边
  • 其余未调整的-1边设为足够大的值避免影响最短路径

这种方法时间复杂度为O(E × (V + E)logV),在约束条件下是可行的。

代码实现

class Solution {
public:
    vector<vector<int>> modifiedGraphEdges(int n, vector<vector<int>>& edges, int source, int destination, int target) {
        vector<vector<pair<int, int>>> graph(n);
        for (int i = 0; i < edges.size(); i++) {
            int u = edges[i][0], v = edges[i][1];
            graph[u].push_back({v, i});
            graph[v].push_back({u, i});
        }
        
        auto dijkstra = [&]() -> int {
            vector<int> dist(n, INT_MAX);
            priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
            dist[source] = 0;
            pq.push({0, source});
            
            while (!pq.empty()) {
                auto [d, u] = pq.top();
                pq.pop();
                if (d > dist[u]) continue;
                
                for (auto [v, idx] : graph[u]) {
                    int w = edges[idx][2];
                    if (w == -1) w = 1;
                    if (dist[u] + w < dist[v]) {
                        dist[v] = dist[u] + w;
                        pq.push({dist[v], v});
                    }
                }
            }
            return dist[destination];
        };
        
        int minDist = dijkstra();
        if (minDist < target) return {};
        if (minDist == target) {
            for (auto& edge : edges) {
                if (edge[2] == -1) edge[2] = 2e9;
            }
            return edges;
        }
        
        for (int i = 0; i < edges.size(); i++) {
            if (edges[i][2] != -1) continue;
            
            edges[i][2] = 1;
            int dist = dijkstra();
            
            if (dist <= target) {
                edges[i][2] = target - dist + 1;
                for (int j = i + 1; j < edges.size(); j++) {
                    if (edges[j][2] == -1) edges[j][2] = 2e9;
                }
                return edges;
            }
        }
        
        return {};
    }
};
class Solution:
    def modifiedGraphEdges(self, n: int, edges: List[List[int]], source: int, destination: int, target: int) -> List[List[int]]:
        import heapq
        
        graph = [[] for _ in range(n)]
        for i, (u, v, w) in enumerate(edges):
            graph[u].append((v, i))
            graph[v].append((u, i))
        
        def dijkstra():
            dist = [float('inf')] * n
            dist[source] = 0
            pq = [(0, source)]
            
            while pq:
                d, u = heapq.heappop(pq)
                if d > dist[u]:
                    continue
                
                for v, idx in graph[u]:
                    w = edges[idx][2]
                    if w == -1:
                        w = 1
                    if dist[u] + w < dist[v]:
                        dist[v] = dist[u] + w
                        heapq.heappush(pq, (dist[v], v))
            
            return dist[destination]
        
        min_dist = dijkstra()
        if min_dist < target:
            return []
        if min_dist == target:
            for edge in edges:
                if edge[2] == -1:
                    edge[2] = 2 * 10**9
            return edges
        
        for i, edge in enumerate(edges):
            if edge[2] != -1:
                continue
            
            edge[2] = 1
            dist = dijkstra()
            
            if dist <= target:
                edge[2] = target - dist + 1
                for j in range(i + 1, len(edges)):
                    if edges[j][2] == -1:
                        edges[j][2] = 2 * 10**9
                return edges
        
        return []
public class Solution {
    public int[][] ModifiedGraphEdges(int n, int[][] edges, int source, int destination, int target) {
        var graph = new List<(int, int)>[n];
        for (int i = 0; i < n; i++) {
            graph[i] = new List<(int, int)>();
        }
        
        for (int i = 0; i < edges.Length; i++) {
            int u = edges[i][0], v = edges[i][1];
            graph[u].Add((v, i));
            graph[v].Add((u, i));
        }
        
        int Dijkstra() {
            var dist = new int[n];
            Array.Fill(dist, int.MaxValue);
            var pq = new PriorityQueue<int, int>();
            dist[source] = 0;
            pq.Enqueue(source, 0);
            
            while (pq.Count > 0) {
                int u = pq.Dequeue();
                
                foreach (var (v, idx) in graph[u]) {
                    int w = edges[idx][2];
                    if (w == -1) w = 1;
                    if (dist[u] != int.MaxValue && dist[u] + w < dist[v]) {
                        dist[v] = dist[u] + w;
                        pq.Enqueue(v, dist[v]);
                    }
                }
            }
            
            return dist[destination];
        }
        
        int minDist = Dijkstra();
        if (minDist < target) return new int[0][];
        if (minDist == target) {
            foreach (var edge in edges) {
                if (edge[2] == -1) edge[2] = 2000000000;
            }
            return edges;
        }
        
        for (int i = 0; i < edges.Length; i++) {
            if (edges[i][2] != -1) continue;
            
            edges[i][2] = 1;
            int dist = Dijkstra();
            
            if (dist <= target) {
                edges[i][2] = target - dist + 1;
                for (int j = i + 1; j < edges.Length; j++) {
                    if (edges[j][2] == -1) edges[j][2] = 2000000000;
                }
                return edges;
            }
        }
        
        return new int[0][];
    }
}
var modifiedGraphEdges = function(n, edges, source, destination, target) {
    const INF = 2e9;
    
    function dijkstra(graph, src, dest) {
        const dist = new Array(n).fill(INF);
        const pq = [[0, src]];
        dist[src] = 0;
        
        while (pq.length > 0) {
            pq.sort((a, b) => a[0] - b[0]);
            const [d, u] = pq.shift();
            
            if (d > dist[u]) continue;
            
            for (const [v, w] of graph[u]) {
                if (dist[u] + w < dist[v]) {
                    dist[v] = dist[u] + w;
                    pq.push([dist[v], v]);
                }
            }
        }
        
        return dist[dest];
    }
    
    function buildGraph(useModified = false) {
        const graph = Array.from({length: n}, () => []);
        for (let i = 0; i < edges.length; i++) {
            const [u, v, w] = edges[i];
            const weight = w === -1 ? (useModified ? 1 : INF) : w;
            graph[u].push([v, weight]);
            graph[v].push([u, weight]);
        }
        return graph;
    }
    
    // Check if already possible without modifying -1 edges
    const graphWithoutModified = buildGraph(false);
    const distWithoutModified = dijkstra(graphWithoutModified, source, destination);
    
    if (distWithoutModified < target) {
        return [];
    }
    
    // Check if possible with all -1 edges set to 1
    const graphWithModified = buildGraph(true);
    const distWithModified = dijkstra(graphWithModified, source, destination);
    
    if (distWithModified > target) {
        return [];
    }
    
    // Binary search approach - modify edges one by one
    const result = [...edges];
    
    for (let i = 0; i < result.length; i++) {
        if (result[i][2] === -1) {
            result[i][2] = 1;
        }
    }
    
    // Build graph with current modifications
    const graph = Array.from({length: n}, () => []);
    for (const [u, v, w] of result) {
        graph[u].push([v, w]);
        graph[v].push([u, w]);
    }
    
    const currentDist = dijkstra(graph, source, destination);
    
    if (currentDist === target) {
        return result;
    }
    
    if (currentDist > target) {
        return [];
    }
    
    // Need to increase some edge weight
    const diff = target - currentDist;
    
    for (let i = 0; i < result.length; i++) {
        if (edges[i][2] === -1) {
            result[i][2] = 1 + diff;
            
            // Rebuild graph
            const newGraph = Array.from({length: n}, () => []);
            for (const [u, v, w] of result) {
                newGraph[u].push([v, w]);
                newGraph[v].push([u, w]);
            }
            
            const newDist = dijkstra(newGraph, source, destination);
            
            if (newDist === target) {
                return result;
            }
            
            if (newDist > target) {
                // Binary search for the right weight
                let left = 1, right = 1 + diff;
                while (left < right) {
                    const mid = Math.floor((left + right) / 2);
                    result[i][2] = mid;
                    
                    const testGraph = Array.from({length: n}, () => []);
                    for (const [u, v, w] of result) {
                        testGraph[u].push([v, w]);
                        testGraph[v].push([u, w]);
                    }
                    
                    const testDist = dijkstra(testGraph, source, destination);
                    
                    if (testDist <= target) {
                        left = mid + 1;
                    } else {
                        right = mid;
                    }
                }
                result[i][2] = left - 1;
                
                const finalGraph = Array.from({length: n}, () => []);
                for (const [u, v, w] of result) {
                    finalGraph[u].push([v, w]);
                    finalGraph[v].push([u, w]);
                }
                
                if (dijkstra(finalGraph, source, destination) === target) {
                    return result;
                }
                
                result[i][2] = left;
                const finalGraph2 = Array.from({length: n}, () => []);
                for (const [u, v, w] of result) {
                    finalGraph2[u].push([v, w]);
                    finalGraph2[v].push([u, w]);
                }
                
                if (dijkstra(finalGraph2, source, destination) === target) {
                    return result;
                }
            }
            
            break;
        }
    }
    
    return result;
};

复杂度分析

复杂度类型分析
时间复杂度O(E × (V + E)logV),其中E为边数,V为节点数。需要对每条-1边进行一次Dijkstra计算
空间复杂度O(V + E),用于存储图的邻接表和Dijkstra算法的距离数组