题意
给出多个形如 a/b=k
的式子以及询问 a/b=?
,要求对回答每个询问的结果,若从式子推导不出询问的答案,则返回-1。其中 a
, b
为任意字符串,k
为实数。例如:
式子: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
思路
不难想到的是将除法变成乘法的形式,例如 a/b=2
可以变为 a=2b
,那么通过将每个字符串表示为其它字符串的倍数,就能得到任意两个之间的倍数关系。例如对于 a/b=2, c/b=3, e/c=3
,可以得到 a=2b, c=3b, e=9b
那么 a/b, a/c, b/c, b/e, c/e
就都能得到。进一步,这种关系可以通过并查集这种数据结构实现,以类似于树的关系将 e
表示成根节点 b
的倍数,即可判断任意两个之间的关系。与并查集类似,在建立关系树的时候要考虑两个子树之间的合并关系:合并子树的根。步骤如下:
- 构造数据结构以表示a=kb这种关系
- 考虑每一个等式
a/b=k
a, b
都不在任一子树中,以b
为根建立子树a, b
其中一个在某个子树中, 找到子树的根,将不在子树上的加入子树a, b
在不同的子树上,找到两颗子树的根,合并子树
- 对每个询问,通过与根的倍数关系得出结果
代码
struct relation {
int who;
double times;
relation(int w, double t) {
who = w, times = t;
}
};
class Solution {
private:
map<string, int> ids;
vector<relation> fathers;
int idtot = 0;
public:
vector<double> calcEquation(vector<vector<string>> &equations, vector<double> &values, vector<vector<string>> &queries) {
for (int i = 0; i < equations.size(); i++) {
setFather(equations[i], values[i]);
}
vector<double> resvec;
for (auto &&vec : queries) {
double ret = queryK(vec[0], vec[1]);
resvec.push_back(ret);
}
return resvec;
}
relation findFaa(int id) {
relation ret = fathers[id];
if (ret.who == id)
return ret;
relation tmp = findFaa(ret.who);
ret.who = tmp.who;
ret.times *= tmp.times;
return ret;
}
void setFather(vector<string> equation, double value) {
string a = equation[0], b = equation[1];
if (ids.find(a) == ids.end() && ids.find(b) == ids.end()) {
int aid = idtot++;
int bid = idtot++;
ids[a] = aid;
ids[b] = bid;
fathers.push_back(relation(bid, value));
fathers.push_back(relation(bid, 1));
} else if (ids.find(a) == ids.end()) {
int aid = idtot++;
int bid = ids[b];
ids[a] = aid;
relation bfa = findFaa(bid);
fathers.push_back(relation(bfa.who, value * bfa.times));
} else if (ids.find(b) == ids.end()) {
int aid = ids[a];
int bid = idtot++;
ids[b] = bid;
relation afa = findFaa(aid);
fathers.push_back(relation(afa.who, (1.0 / value) * afa.times));
} else {
int aid = ids[a], bid = ids[b];
relation afa = findFaa(aid), bfa = findFaa(bid);
if (afa.who != bfa.who) {
fathers[afa.who].times = bfa.times * value / afa.times;
fathers[afa.who].who = bfa.who;
}
}
}
double queryK(string a, string b) {
if (ids.find(a) == ids.end() || ids.find(b) == ids.end())
return -1;
int aid = ids[a], bid = ids[b];
relation afa = findFaa(aid), bfa = findFaa(bid);
if (afa.who != bfa.who)
return -1;
return afa.times / bfa.times;
}
};