LeetCode-399. Evaluación de la división

descripción

Proporcionarle una matriz de ecuaciones de pares de variables y una matriz de valores de valores reales como condiciones conocidas, donde las ecuaciones [i] = [Ai, Bi] y los valores [i] juntos representan la ecuación Ai / Bi = valores [i ]. Cada Ai o Bi es una cadena que representa una sola variable.

También hay algunas preguntas representadas por una serie de consultas, donde las consultas [j] = [Cj, Dj] representan la j-ésima pregunta. Encuentre el resultado de Cj / Dj =? Como la respuesta basada en las condiciones conocidas.

Devuelva las respuestas a todas las preguntas. Si hay una respuesta insegura, reemplace esta respuesta con -1.0.

 

Nota: La entrada siempre es válida. Puede suponer que no habrá divisor 0 en la operación de división y que no habrá resultados contradictorios.

 

Ejemplo 1:

输入 : ecuaciones = [["a", "b"], ["b", "c"]], valores = [2.0,3.0], consultas = [["a", "c"], ["b "," a "], [" a "," e "], [" a "," a "], [" x "," x "]]
输出 : [6.00000,0.50000, -1.00000,1.00000, - 1.00000]
解释 :
条件 : a / b = 2.0, b / c = 3.0
问题 : a / c =?, B / a =?, A / e =?, A / a =?, X / x =?
结果 : [6.0, 0.5, -1.0, 1.0, -1.0]
示例 2 :

输入 : ecuaciones = [["a", "b"], ["b", "c"], ["bc", "cd"]], valores = [1.5,2.5,5.0], consultas = [[ "a", "c"], ["c", "b"], ["bc", "cd"], ["cd", "bc"]]
输出 : [3.75000,0.40000,5.00000,0.20000]
示例 3 :

输入 : ecuaciones = [["a", "b"]], valores = [0.5], consultas = [["a", "b"], ["b", "a"], ["a", "c"], ["x", "y"]]
输出 : [0.50000,2.00000, -1.00000, -1.00000]
 

rápido:

1 <= ecuaciones.longitud <= 20
ecuaciones [i] .length == 2
1 <= Ai.length, Bi.length <= 5
valores.length == ecations.length
0.0 <valores [i] <= 20.0
1 < = queries.length <= 20
consultas [i] .length == 2
1 <= Cj.length, Dj.length <= 5
Ai, Bi, Cj, Dj 由 小写 英文 字母 与 数字 组成

Fuente: LeetCode
Enlace: https://leetcode-cn.com/problems/evaluate-division/

Nota especial

Se utilizan tres métodos en la solución. Los métodos uno y dos utilizan la búsqueda de amplitud primero, pero los métodos de almacenamiento de las dos implementaciones son diferentes. El método uno utiliza el almacenamiento de matriz de adyacencia, el método dos utiliza el almacenamiento de tabla de adyacencia; el método tres utiliza el algoritmo flyod, O La manera es encontrar la ruta más corta entre dos puntos en el gráfico, es decir, puede haber múltiples rutas en el gráfico y los valores pueden ser inconsistentes. ¿Por qué se puede usar para resolver este problema? ? ? Preste especial atención a la descripción de la pregunta: "La entrada es siempre válida. Puede suponer que no habrá divisor 0 en la operación de división y que no hay resultado contradictorio", ¡no hay resultado contradictorio! ! ! No debe haber ninguna ruta entre los dos puntos o solo hay una ruta. Por supuesto, el método anterior se puede utilizar para resolver el problema.

Resolver

    class Solution {
    private:
        const double INVALID = -1.0;

        // 方法一辅助函数
        // 对图graph从顶点 v开始进行广度优先遍历,直到遇到w结束或者直到遍历完成
        // 注意,这儿是从某个顶点开始广度优先遍历,并非对整个图进行广度遍历
        void bfsTraverse(vector<vector<double>> &graph, int a, int b) {
            const int N = graph.size();
            // 广度优先遍历并更新权值
            vector<bool> visited(N, false);
            queue<int> q;
            q.push(a);
            visited[a] = true;
            // 一旦遍历到b点,立即结束遍历
            while (!q.empty() && !visited[b]) {
                int v = q.front();
                q.pop();
                // 遍历v邻接点
                for (int j = 0; j < N; ++j) {
                    // 1)j未被遍历 2)j是v的邻接点 3)a到v可达,这样才可能a->v->j
                    if (graph[v][j] != INVALID && !visited[j] && graph[a][v] != INVALID) {
                        graph[a][j] = graph[a][v] * graph[v][j];
                        q.push(j);
                        visited[j] = true;
                    }
                }
            }
        }

    public:
        // 方法一,构造有向带权图实现,采用邻接矩阵存储
        // 缺点:1)存储采用邻接矩阵有点浪费
        // 2)每次为了遍历一个新的a -> b,拷贝了一份图的存储,目的是遍历过程中修改值,存储和效率上的瑕疵
        vector<double>
        calcEquation_1e(vector<vector<string>> &equations, vector<double> &values, vector<vector<string>> &queries) {
            if (equations.empty()) {
                return vector<double>();
            }
            int vIndex = 0; // 顶点初始编号
            unordered_map<string, int> vHashMap; // 顶点到数值的映射
            for (auto &vec : equations) {
                if (vHashMap.count(vec[0]) == 0) {
                    vHashMap.emplace(vec[0], vIndex++);
                }
                if (vHashMap.count(vec[1]) == 0) {
                    vHashMap.emplace(vec[1], vIndex++);
                }
            }

            // 用二维矩阵即邻接矩阵存储图的信息
            vector<vector<double>> graph(vIndex, vector<double>(vIndex, INVALID));
            for (int i = 0; i < vIndex; ++i) {
                graph[i][i] = 1.0;
            }
            for (int i = 0; i < equations.size(); ++i) {
                int v = vHashMap[equations[i][0]];  // 顶点v的数值映射
                int w = vHashMap[equations[i][1]];  // 顶点w的数值映射
                graph[v][w] = values[i];
                graph[w][v] = 1.0 / values[i];
            }

            vector<double> res;
            for (int i = 0; i < queries.size(); ++i) {
                if ((vHashMap.count(queries[i][0])) > 0 && (vHashMap.count(queries[i][1]) > 0)) {
                    // 待求得点均在已构建的图中
                    auto gcp = graph;
                    int a = vHashMap[queries[i][0]];
                    int b = vHashMap[queries[i][1]];
                    bfsTraverse(gcp, a, b);
                    res.push_back(gcp[a][b]);
                    continue;
                }
                // 待求点未知
                res.push_back(-1);
            }
            return res;
        }

        // 方法二,构造有向带权图实现,采用邻接表实现,且遍历中不再采用辅助函数,也不用额外拷贝图的存储
        vector<double>
        calcEquation_2e(vector<vector<string>> &equations, vector<double> &values, vector<vector<string>> &queries) {
            if (equations.empty()) {
                return vector<double>();
            }
            int vIndex = 0; // 顶点初始编号
            unordered_map<string, int> vHashMap; // 顶点到数值的映射
            for (auto &vec : equations) {
                if (vHashMap.count(vec[0]) == 0) {
                    vHashMap.emplace(vec[0], vIndex++);
                }
                if (vHashMap.count(vec[1]) == 0) {
                    vHashMap.emplace(vec[1], vIndex++);
                }
            }

            // 采用邻接表构造图
            vector<vector<std::pair<int, double>>> edges(vIndex);
            for (int i = 0; i < equations.size(); ++i) {
                int v = vHashMap[equations[i][0]];  // 顶点v的数值映射
                int w = vHashMap[equations[i][1]];  // 顶点w的数值映射
                edges[v].emplace_back(w, values[i]);
                edges[w].emplace_back(v, 1.0 / values[i]);
            }

            // 根据待求每对点进行广度优先遍历
            vector<double> res;
            for (int i = 0; i < queries.size(); ++i) {
                // 待求点均在已构建的图中
                if ((vHashMap.count(queries[i][0])) > 0 && (vHashMap.count(queries[i][1]) > 0)) {
                    int a = vHashMap[queries[i][0]];
                    int b = vHashMap[queries[i][1]];
                    // 待求点相等,无需遍历
                    if (a == b) {
                        res.push_back(1.0);
                        continue;
                    }
                    // 待求点不等,需要遍历
                    vector<double> ratios(vIndex, -1.0);
                    queue<int> q;
                    q.push(a);
                    ratios[a] = 1.0;
                    while (!q.empty() && (ratios[b] == -1.0)) {
                        int v = q.front();
                        q.pop();
                        // 关注下一行的C++新特性
                        for (const auto &[w, weight] : edges[v]) {
                            // 遍历v的所有邻接点
                            if (ratios[w] == -1.0) {
                                ratios[w] = ratios[v] * weight;
                                q.push(w);
                            }
                        }
                    }
                    res.push_back(ratios[b]);
                    continue;
                }
                // 待求点未知,即不在构建的图中
                res.push_back(-1);
            }
            return res;
        }

        // 方法三,构造有向带权图实现,采用邻接矩阵存储,使用floyd算法求出任意两点间的值(假如存在),而不用每次进行遍历查询
        vector<double>
        calcEquation(vector<vector<string>> &equations, vector<double> &values, vector<vector<string>> &queries) {
            if (equations.empty()) {
                return vector<double>();
            }
            int vIndex = 0; // 顶点初始编号
            unordered_map<string, int> vHashMap; // 顶点到数值的映射
            for (auto &vec : equations) {
                if (vHashMap.count(vec[0]) == 0) {
                    vHashMap.emplace(vec[0], vIndex++);
                }
                if (vHashMap.count(vec[1]) == 0) {
                    vHashMap.emplace(vec[1], vIndex++);
                }
            }

            // 用二维矩阵即邻接矩阵存储图的信息
            vector<vector<double>> graph(vIndex, vector<double>(vIndex, INVALID));
            for (int i = 0; i < vIndex; ++i) {
                graph[i][i] = 1.0;
            }
            for (int i = 0; i < equations.size(); ++i) {
                int v = vHashMap[equations[i][0]];  // 顶点v的数值映射
                int w = vHashMap[equations[i][1]];  // 顶点w的数值映射
                graph[v][w] = values[i];
                graph[w][v] = 1.0 / values[i];
            }

            // floyd算法遍历,求出任意两点间的值
            for (int i = 0; i < vIndex; ++i) {
                for (int v = 0; v < vIndex; ++v) {
                    for (int w = 0; w < vIndex; ++w) {
                        if (graph[v][i] != INVALID && graph[i][w] != INVALID) {
                            graph[v][w] = graph[v][i] * graph[i][w];
                        }
                    }
                }
            }

            // 通过表查询待求点对的值
            vector<double> res;
            for (int i = 0; i < queries.size(); ++i) {
                // 待求点均在已构建的图中
                if ((vHashMap.count(queries[i][0])) > 0 && (vHashMap.count(queries[i][1]) > 0)) {
                    int a = vHashMap[queries[i][0]];
                    int b = vHashMap[queries[i][1]];
                    res.push_back(graph[a][b]);
                    continue;
                }
                // 待求点未知,即不在构建的图中
                res.push_back(-1);
            }
            return res;
        }
    };

 

Supongo que te gusta

Origin blog.csdn.net/u010323563/article/details/112285984
Recomendado
Clasificación