LeetCode 399. 除法求值(邊帶權並查集、DFS、建圖、字符串映射)

除法求值

DFS

版本1

首先用map<string,int>將字符串變量映射爲數字。

建圖:由於題目中,並沒有數據都不爲0,所以我乾脆用bool g[][]來記錄邊的存在,用double graph[][]來記錄邊權,並且建成雙向圖。

dfs:
bool vis[]記錄訪問的點,防止反覆橫跳,無限遞歸下去。
回溯的時候,注意還原現場

class Solution {
public:
    map<string,int> m;
    double graph[1010][1010] = {0};
    bool g[1010][1010] = {0},vis[1010] = {0};
    int cnt = 0;
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {

        for(int i=0;i<equations.size();i++){
            vector<string> &pair = equations[i];
            int x,y;
            if(m.count(pair[0])){
                x = m[pair[0]];
            }else{
                m[pair[0]] = ++cnt;
                x = cnt;
            }
            if(m.count(pair[1])){
                y = m[pair[1]];
            }else{
                m[pair[1]] = ++cnt;
                y = cnt;
            }
            g[x][y] = g[y][x] =1;
            graph[x][y] = values[i];
            graph[y][x] = values[i]==0? -1:1.0/values[i];
        }
        vector<double> ans;
        for(auto pair:queries){
            string &s1 = pair[0];
            string &s2 = pair[1];
            if(!m.count(s1) || !m.count(s2)){
                ans.push_back(-1);
                continue; 
            }
            
            int x = m[s1];
            int y = m[s2];
            memset(vis,0,sizeof(vis)); //多次dfs,多次清空標記數組。
            double res = 1; 
            if(!dfs(x,y,res)){    //先判斷是否連通
                ans.push_back(-1);
            }else{
                ans.push_back(res);
            }
            
            
        }
        return ans;
    }

    bool dfs(int x,int y,double &res){
        if(x==y){
            return true;
        }
        vis[x] = 1;
        for(int i=1;i<=cnt;i++){
            if(g[x][i] && !vis[i]){
                res*=graph[x][i];
                if(dfs(i,y,res)){
                    return true;
                }
                res/=graph[x][i];
                vis[i] = 0;
            }
        }
        return false;
    }
};

版本2

上個版本的DFS是將字符串映射到數字,看了網友的做法,其實也沒有必要將字符串映射成數字去處理。

如何建圖:
如果用鄰接矩陣存儲圖的話,int graph[][]map<string,map<string,int>>代替,或者用unordered_map<string,unordered_map<string,double>>代替(內部不用排序,效率更高)。
如何防止冗餘:
之前用bool visited[]判斷一個點有沒有搜索過,現在用set<string>即可。

class Solution {
public:
    unordered_map<string,unordered_map<string,double>> graph;
    vector<double> ans;
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
        for(int i=0;i<equations.size();i++){
            auto &edge = equations[i];
            graph[edge[0]][edge[1]] = values[i];
            graph[edge[1]][edge[0]] = 1/values[i];   
        }
        for(auto query:queries) {
            unordered_set<string> vis;
            if(!graph.count(query[0]) || !graph.count(query[1]) ){
                ans.push_back(-1);
                continue;
            }
            double res = 1;
            if(!dfs(query[0],query[1],res,vis)){
                ans.push_back(-1);
                continue;                
            }
            ans.push_back(res);
        }
        return ans;
    }
    // a是搜索的起點,b是目標點
    bool dfs(string a,string b,double &res,unordered_set<string> vis){
        if(a==b){
            return true;
        }
        vis.insert(a);
        unordered_map<string,double> & ma = graph[a];
        for(unordered_map<string,double>::iterator it=ma.begin();it!=ma.end();it++){
            if(!vis.count(it->first)){
                res*= it->second;
                if(dfs(it->first,b,res,vis)){
                    return true;
                }
                res/= it->second;
                vis.erase(it->first);
            }
        }
        return false;
    }
};

並查集(未路徑壓縮)

在這裏插入圖片描述
這裏並沒有進行路徑壓縮,嚴格來講查詢的效率降低了很多。

class Solution {
public:
    unordered_map<string,string> father;
    unordered_map<string,double> weights;
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
        vector<double> ans;
        for(int i=0;i<equations.size();i++){
            auto &equation = equations[i];
            string a = equation[0];
            string b = equation[1];
            if(!father.count(a)){
                father[a] = a;
                weights[a] = 1;
            }
            if(!father.count(b)){
                father[b] = b;
                weights[b] = 1;
            }
            merge(a,b,values[i]);
        }
        for(auto &query:queries){
            string a = query[0];
            string b = query[1];
            if(!father.count(a) || !father.count(b)){
                ans.push_back(-1);
                continue;
            }
            auto pa = find(a);
            auto pb = find(b);
            if(pa.first!=pb.first){
                ans.push_back(-1);  //不連通
                continue;
            }
            // a/b = (a/r) / (b/r)             
            ans.push_back(pa.second/pb.second);
        }
        return ans;
    }

    pair<string,double> find(string a){
        double res = 1;
        while(a!=father[a]){
            res*=weights[a];
            a = father[a];

        }
        return make_pair(a,res);
    }

    // ar/br = (b/br) / (a/ar) * (a/b)
    void merge(string a,string b,double a_b){
        auto pa = find(a);
        auto pb = find(b);
        father[pa.first] = pb.first;
        weights[pa.first] = pb.second/pa.second*a_b;
    }

};

並查集(路徑壓縮)
那如果要路徑壓縮呢?
find函數改成這樣既可。

    pair<string,double> find(string a){
        if(a==father[a]){
            return make_pair(a,1);
        }
        auto pa_father = find(father[a]);
        father[a] = pa_father.first;
        weights[a] = pa_father.second*weights[a];
        return make_pair(father[a],weights[a]);
    }

完整代碼:

class Solution {
public:
    unordered_map<string,string> father;
    unordered_map<string,double> weights;
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
        vector<double> ans;
        for(int i=0;i<equations.size();i++){
            auto &equation = equations[i];
            string a = equation[0];
            string b = equation[1];
            if(!father.count(a)){
                father[a] = a;
                weights[a] = 1;
            }
            if(!father.count(b)){
                father[b] = b;
                weights[b] = 1;
            }
            merge(a,b,values[i]);
        }
        for(auto &query:queries){
            string a = query[0];
            string b = query[1];
            if(!father.count(a) || !father.count(b)){
                ans.push_back(-1);
                continue;
            }
            auto pa = find(a);
            auto pb = find(b);
            if(pa.first!=pb.first){
                ans.push_back(-1);  //不連通
                continue;
            }
            // a/b = (a/r) / (b/r)             
            ans.push_back(pa.second/pb.second);
        }
        return ans;
    }

    pair<string,double> find(string a){
        if(a==father[a]){
            return make_pair(a,1);
        }
        auto pa_father = find(father[a]);
        father[a] = pa_father.first;
        weights[a] = pa_father.second*weights[a];
        return make_pair(father[a],weights[a]);
    }

    // ar/br = (b/br) / (a/ar) * (a/b)
    void merge(string a,string b,double a_b){
        auto pa = find(a);
        auto pb = find(b);
        father[pa.first] = pb.first;
        weights[pa.first] = pb.second/pa.second*a_b;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章