zoukankan      html  css  js  c++  java
  • leetcode 399. Evaluate Division(除法求值) [带权并查集]

    给出方程式 A / B = k, 其中 A 和 B 均为代表字符串的变量, k 是一个浮点型数字。根据已知方程式求解问题,并返回计算结果。如果结果不存在,则返回 -1.0。

    示例 :
    给定 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 ]

    输入为: vector<pair<string, string>> equations, vector& values, vector<pair<string, string>> queries(方程式,方程式结果,问题方程式), 其中 equations.size() == values.size(),即方程式的长度与方程式结果长度相等(程式与结果一一对应),并且结果值均为正数。以上为方程式的描述。 返回vector类型。

    基于上述例子,输入如下:

    equations(方程式) = [ [“a”, “b”], [“b”, “c”] ],
    values(方程式结果) = [2.0, 3.0],
    queries(问题方程式) = [ [“a”, “c”], [“b”, “a”], [“a”, “e”], [“a”, “a”], [“x”, “x”] ].
    输入总是有效的。你可以假设除法运算中不会出现除数为0的情况,且不存在任何矛盾的结果。


    题解: 首先可以很容易想到,给出的关系式可以用于建图,变量名即节点,关系式结果即两变量名的权值.并且为有向边,如a / b = 2.0即 a–>b,边权为2.0,而b–>a边权为0.5.

    并且这样的关系具有传递性如 a—2.0—>b—3.0—>c,那么a---->c的权值即2.0*3.0=6.0

    相反,c—>a 的权值同样为1/6,即a—>c的倒数.

    在这个基础上,我们可以构造一棵树,或者更准确的说是一个DAG(有向无环图),你可能会问,为什么是树不是图,并且这个关系式的构造方式明显可以形成环.
    可以这样想,根据给出的关系式,已经确定了两者关系是 [前者 / 后者 = 权值] 那么在建图时就一定要建立 前者 —> 后者 ,val=权值 的边吗?答案是否定的,我们完全可以根据 原式逆向建单项边 后者 —> 前者 ,val=1/权值

    如此以来建图的方式就很灵活了,这样构造出DAG是完全没问题的.

    再者,我们将其抽象成一棵树,从存在相互关系的变量集合中任选一个变量作为根节点. 这样有一个好处,从根节点到达树上任意节点的结果都可以传递性预处理出来,也就是可以将路径压缩. 而树上任意两个节点A、B的关系,等于 val[root---->A] / val[root---->B]val[root---->B] / val[root---->A]

    存储方式不需要直接建图,而是用并查集实现,并且可以实现路径压缩,将每个集合所有节点的val压缩成与根节点的val.

    拿代码中的并查集方式来举例子.
    首先将变量名映射为整型节点,默认值较小的节点为父节点.
    假设D的序号相对C更小,作为C的父节点
    给出关系 A / B = X , C / D = Y
    那么val[B] = A / B =X , val[C] = D / C = 1 / Y

    当要求值 B / C 时, 实际上是 (B / Z) * (Z / C)
    假设我们已经获取了路径压缩的值 val[B] = Z / B , val[C] = Z / C

    最终的结果即 B / C = val[C] / val[B]
    在这里插入图片描述
    在输入关系时进行路径压缩,并计算压缩后的权值.
    最后遍历查询,直接判断是否有解,有解即用val中压缩好的权值相除即可

    class Solution {
    public:
        vector<int>fa;
        vector<double>val;
        int finds(int z)//并查集查询并路径压缩
        {
            if(fa[z]==z)
            {
                return z;
            }
            else
            {
                int tmp=fa[z];
                fa[z]=finds(fa[z]);
                val[z]*=val[tmp];
                return fa[z];
            }
        }
        vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
            map<string,int>m;
            m.clear();
            fa.clear();
            val.clear();
            int num=0;
            vector<double>ans;
            val.push_back(0);
            fa.push_back(0);
            for(int i=0;i<equations.size();i++)
            {
                string a=equations[i][0];
                string b=equations[i][1];
                if(m[a]==0)
                {
                    m[a]=++num;
                    fa.push_back(num);
                    val.push_back(1);
                }
                if(m[b]==0)
                {
                    m[b]=++num;
                    fa.push_back(num);
                    val.push_back(1);
                }
                if(m[a]<m[b])
                {
                    val[m[b]]=values[i];
                    fa[m[b]]=m[a];
                    fa[m[b]]=finds(m[b]);//路径压缩
                }
                else 
                {
                    val[m[a]]=1.0/values[i];//反边
                    fa[m[a]]=m[b];
                    fa[m[a]]=finds(m[a]);//路径压缩
                }
            }
            
            for(int i=0;i<queries.size();i++)
            {
                string a=queries[i][0];
                string b=queries[i][1];
                if(m[a]==0||m[b]==0)//未出现在关系式中底变量,直接无解
                {
                    ans.push_back(-1.0);
                    continue;
                }
                fa[m[a]]=finds(m[a]);
                fa[m[b]]=finds(m[b]);
                if(fa[m[a]]!=fa[m[b]])//不在同一个集合中的变量,没有任何关系,无解
                {
                    ans.push_back(-1.0);
                    continue;
                }
                double aa=val[m[a]];
                double bb=val[m[b]];
                ans.push_back(bb/aa);
            }
            return ans;
        }
    };
    
  • 相关阅读:
    Asp.net 通过Repeater循环添加对应的一组控件
    JS将number数值转化成为货币格式
    Asp.net 在 Postback 之后 执行 javascript 方法
    Asp.net 通过Repeater嵌套Repeater循环添加对应的一组控件
    向SharePoint 2010中添加Permission Level,Group,以及相应的User
    Asp.net 前后台操作cookie 实现数据的循环下载
    JS 将 string 转换成为 number
    C# Dictionary通过value获取对应的key值
    手机相机下的世界
    自定义Data Service Providers — (3)IServiceProvider和DataSources 服务提供者和数据源
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135646.html
Copyright © 2011-2022 走看看