修改图中的边权

给你一个n节点的无向带权连通图,同时告诉你边的端点和权值
对于部分权为-1的边,可以进行修改为任意值,最后使得初始点到目标点最短距离为target

1. Dijkstra

第一次使用迪杰斯特拉算法,将所有能修改的权值视作1,计算到各点的最短距离
判断该距离是否有操作空间,满足要求的情况下计算要修改的量delta
第二次使用迪杰斯特拉算法,因为每次要从最短的路径进行修改
尝试将能修改的边都进行修改,使到下一个点路径比原来最优(注意是原来,而不是第二次修改后)的要增加delta
最后判断终点最短路径是否满足要求

邻接表存储
class Solution {
public:
    vector<vector<int>> modifiedGraphEdges(int n, vector<vector<int>> &edges, int source, int destination, int target) {
        vector<pair<int, int>> g[n]; //邻接表
        for (int i = 0; i < edges.size(); i++) { // 遍历所有边
            int x = edges[i][0], y = edges[i][1];
            g[x].emplace_back(y, i);
            g[y].emplace_back(x, i); // 建图,额外记录边的编号,用于后面修改和返回
        }


        int dis[n][2], delta, vis[n]; 
    
        memset(dis, 0x3f, sizeof(dis));
        dis[source][0] = dis[source][1] = 0; //初始化初始节点距离
        auto dijkstra = [&](int k) { // 这里 k 表示第一次/第二次
            memset(vis, 0, sizeof(vis)); //初始都未访问
            for (;;) {//因为是连通图,跳出条件为找到目标值
                int x = -1; //用于贪心找点
                for (int i = 0; i < n; ++i) //遍历所有点
                    if (!vis[i] && (x < 0 || dis[i][k] < dis[x][k])) //找未标记的最近点
                        x = i;
                if (x == destination) // 起点 source 到终点 destination 的最短路已确定
                    return;
                vis[x] = true; // 标记,在后续的循环中无需反复更新 x 到其余点的最短路长度
                for (auto [y, id]: g[x]) {  //遍历x的相邻边
                    int weight = edges[id][2];  //获取边的权值,这里要修正,但不修改,所以需要暂存,而不是直接使用权值去更新
                    if (weight == -1)  weight = 1; // -1 改成 1,第一次不直接在edge上改,防止丢失修改目标

                    if (k == 1 && edges[id][2] == -1) { //对于可以进行修正的边
                        // 第二次 Dijkstra,修正,是满足目标值
                        int w = delta + dis[y][0] - dis[x][1]; //修正的权值
                        if (w > weight)
                            edges[id][2] = weight = w; // 直接在 edges 上修改
                    }

                    // 更新最短路
                    dis[y][k] = min(dis[y][k], dis[x][k] + weight);
                }
            }
        };

        dijkstra(0);
        delta = target - dis[destination][0]; //还需要增加的值
        if (delta < 0) // -1 全改为 1 时,最短路比 target 还大
            return {};

        dijkstra(1);
        if (dis[destination][1] < target) // 最短路无法再变大,无法达到 target
            return {};

        for(auto&edge:edges) //按题目要求修改所有-1权值
            if(edge[2]==-1) edge[2] = 1;


        return edges;
    }
};

邻接矩阵

class Solution {
public:
    vector<vector<int>> modifiedGraphEdges(int n, vector<vector<int>>& edges, int source, int destination, int target) {
        vector<vector<int>> graph(n,vector<int>(n,INT_MAX));
        for(int i=0;i<edges.size();i++){
            int from = edges[i][0];
            int to = edges[i][1];
            graph[from][to] = i;//这里存储权值的索引,方便修改
            graph[to][from] = i;
        }

        vector<int> dis(n,INT_MAX);//初始距离无限远
        vector<bool> vis(n);
        dis[source] = 0;
        //第一轮Dihkstra
        for(int times=0;times<n;times++){//最多遍历n次
            int cur = -1;
            for(int i=0;i<n;i++){//贪心找最小值
                if(vis[i]||dis[i]==INT_MAX) continue;//跳过已经选取的点和无法到达的点
                if(cur==-1||dis[i]<dis[cur]) cur = i;
            }
            if(cur==-1) return {};//表示无法到达目标,在该题中不会出现这种情况,因为是连通图
            //if(cur==destination) break; //找到目标直接跳出,此时dis[cur]即为最短路径,无需再更新
            vis[cur] = 1;
            //找到当前最近点后,根据最近点进行更新
            for(int i=0;i<n;i++){
                if(vis[i]||graph[cur][i]==INT_MAX) continue;//对应点已选取,或对应的边不存在,无需更新
                int weight = edges[graph[cur][i]][2];//获取对应边权值
                if(weight==-1) weight = 1; 
                dis[i] = min(dis[i],dis[cur]+weight);
            }
        }

        int delta = target - dis[destination];//还需要变化的量
        if(delta<0) return {};//还需要减少,无法实现

        vector<int> newdis(n,INT_MAX);//新的最短距离数组,用于跟原来的对比更新权值
        vis.assign(n,0);//重置
        newdis[source] = 0;//初始化
        //第二轮Dijkstra,修改边使最短路径成为目标值
        for(int times=0;times<n;times++){//最多遍历n次
            int cur = -1;
            for(int i=0;i<n;i++){//贪心找最小值
                if(vis[i]||newdis[i]==INT_MAX) continue;//跳过已经选取的点和无法到达的点
                if(cur==-1||newdis[i]<newdis[cur]) cur = i;
            }
            if(cur==destination) break; //找到目标直接跳出,此时dis[cur]即为最短路径,无需再更新

            vis[cur] = 1;
            //找到当前最近点后,根据最近点进行更新
            for(int i=0;i<n;i++){ //也就是计算cur->i两点的距离,与原来的进行更新
                if(vis[i]||graph[cur][i]==INT_MAX||dis[i]==INT_MAX) continue;//对应点已选取,或对应的边不存在,无需更新
                int weight = edges[graph[cur][i]][2];//获取对应边权值
                if(weight==-1)
                    weight = max(1,delta + dis[i]-newdis[cur]);//进行修正,使得newdis[i]比原来增加delta
                edges[graph[cur][i]][2] = weight;//改回到返回值中
                newdis[i] = min(newdis[i],newdis[cur]+weight);
            }
        }
        if(newdis[destination]<target) return {};
        for(auto&edge:edges)//根据提议要求修改所有-1
            if(edge[2]==-1) edge[2]=1;
        return edges;
    }
};
posted @ 2023-05-23 01:35  失控D大白兔  阅读(35)  评论(0编辑  收藏  举报