LeetCode HOT100 - 除法求值
比较直观的想法就是建图,遇到查询就 dfs 跑一下图
因为题目保证了答案不冲突,所以直接跑就可以
考虑 dfs 实际上是自底向上考虑,也就是父节点得到子节点的什么信息,结合自身节点的信息来得到答案
class Solution {
public:
vector<double> calcEquation(vector<vector<string>>& a, vector<double>& b, vector<vector<string>>& q) {
unordered_map<string, vector<pair<string, double>>> adj;
unordered_set<string> st;
for (int i = 0; i < a.size(); i++) {
string u = a[i][0], v = a[i][1];
adj[u].emplace_back(v, b[i]);
adj[v].emplace_back(u, 1.0 / b[i]);
st.insert(u);
st.insert(v);
}
vector<double> ans;
auto dfs = [&](auto self, string x, string y, unordered_set<string>& vis) -> double {
if (x == y) return 1.0;
vis.insert(x);
for (auto [nxt, val] : adj[x]) {
if (vis.count(nxt)) continue;
double res = self(self, nxt, y, vis);
if (res != -1.0) {
return res * val;
}
}
return -1.0;
};
for (auto i : q) {
string x = i[0], y = i[1];
if (st.find(x) == st.end() || st.find(y) == st.end()) {
ans.emplace_back(-1.0);
continue;
}
unordered_set<string> vis;
double res = dfs(dfs, x, y, vis);
ans.emplace_back(res);
}
return ans;
}
};
正解是带权并查集
如果这题没有要我们求具体的解,仅问能否根据我们已有的信息去达到询问的答案
那么显然就是使用并查集
但现在我们想要答案,也就是我们需要有权重,比如从子节点到根节点的权重,因此,也就是带权并查集
实际上也不限于权值,带权并查集是在在普通并查集“只维护谁和谁连通”的基础上,再额外维护“节点到父节点(或到根)的相对关系”。
这个关系常见的有
- 距离差:dist[y] - dist[x] = w
- 奇偶关系:color[x] xor color[y] = 0/1
- 模 3 关系:食物链那种 type[y] - type[x] ≡ w (mod 3)
d[x] 表示 x 到根节点的势能差,即 val[x] - val[f[x]]
find 的时候,路径压缩要顺便更新 d[x]
merge 的时候要根据题目的关系,推导根和根之间应该满足什么关系
#include <bits/stdc++.h>
using namespace std;
struct DSU {
std::vector<int> f, siz;
std::vector<double> d;
// d[x] = val[x] / val[root(x)](在 find 之后成立)
// 更准确地说,平时 d[x] 是 val[x] / val[f[x]]
// 路径压缩后会更新成 val[x] / val[root]
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
d.assign(n, 1.0); // 乘法单位元是 1
}
int find(int x) {
if (x == f[x]) {
return x;
}
int p = f[x];
f[x] = find(f[x]);
d[x] *= d[p];
return f[x];
}
bool same(int x, int y) {
return find(x) == find(y);
}
// 返回 val[x] / val[y]
double ratio(int x, int y) {
find(x);
find(y);
return d[x] / d[y];
}
// 添加关系:val[x] / val[y] = w
bool merge(int x, int y, double w) {
int rx = find(x);
int ry = find(y);
if (rx == ry) {
return std::fabs(ratio(x, y) - w) < 1e-9;
}
// 按大小合并
if (siz[rx] < siz[ry]) {
std::swap(rx, ry);
std::swap(x, y);
w = 1.0 / w;
}
// 现在把 ry 挂到 rx 下
//
// 已知:
// val[x] / val[y] = w
// d[x] = val[x] / val[rx]
// d[y] = val[y] / val[ry]
//
// 设要维护:
// d[ry] = val[ry] / val[rx]
//
// 则:
// val[x] = d[x] * val[rx]
// val[y] = d[y] * val[ry]
//
// 所以:
// (d[x] * val[rx]) / (d[y] * val[ry]) = w
// => val[ry] / val[rx] = d[x] / (w * d[y])
//
// 即:
// d[ry] = d[x] / (w * d[y])
siz[rx] += siz[ry];
f[ry] = rx;
d[ry] = d[x] / (w * d[y]);
return true;
}
int size(int x) {
return siz[find(x)];
}
};
class Solution {
public:
vector<double> calcEquation(vector<vector<string>>& a, vector<double>& b, vector<vector<string>>& q) {
vector<string> c;
for (auto &i : a) {
c.emplace_back(i[0]);
c.emplace_back(i[1]);
}
sort(c.begin(), c.end());
c.erase(unique(c.begin(), c.end()), c.end());
auto get = [&](const string &s) -> int {
return lower_bound(c.begin(), c.end(), s) - c.begin();
};
auto exist = [&](const string &s) -> bool {
return binary_search(c.begin(), c.end(), s);
};
DSU dsu(c.size());
for (int i = 0; i < a.size(); i++) {
int x = get(a[i][0]);
int y = get(a[i][1]);
dsu.merge(x, y, b[i]);
}
vector<double> ans;
for (auto i : q) {
string x = i[0], y = i[1];
if (!exist(x) || !exist(y)) {
ans.push_back(-1.0);
continue;
}
int u = get(x);
int v = get(y);
if (!dsu.same(u, v)) {
ans.push_back(-1.0);
} else {
ans.push_back(dsu.ratio(u, v));
}
}
return ans;
}
};

浙公网安备 33010602011771号