以前写的几道力扣题的题解
leetcode-1719
题目关键点:
- 树对
pairs[i]=[x_i,y_i]
,表示一个结点与其祖先,其中祖先可以是两者中的任意一个 - 别搞错了祖先的定义:
父亲(parent node):对于除根以外的每个结点,定义为从该结点到根路径上的第二个结点。 根结点没有父结点。
祖先(ancestor):一个结点到根结点的路径上,除了它本身外的结点。 根结点的祖先集合为空。 - pairs 包含树中所有可能构成祖先的树对。
n:树中节点数
degree[x]:pairs 中包含节点 x 的数对的数目
adj[x]:节点 x 的祖先和后代的节点集合
degree[x] = adj[x].size()
,adj[x] 是个 vector,存储的是节点,因此是个集合;degree[x] 是个数字,即 adj[x] 的大小。
找出能够构成有根树的充分必要条件即可。
重点在于 degree 的性质:
degree[root]=n-1
根节点与其他所有节点都能构成树对;adj[root]={node[1],node[2],...,node[n-1]}
树中除了它自己,其余的都是后代节点,并且没有祖先。- 对于数对
[x_i,y_i]
,如果x_i
是y_i
的祖先,则一定满足degree[x_i]\geq degree[y_i], adj[y_i]\in adj[x_i]
。 - 对于数对
[x_i,y_i]
,如果x_i
是y_i
的祖先,且degree[x_i]=degree[y_i], adj[x_i]=adj[y_i]
,则x_i
到y_i
之间到路径上均只有一个节点。即,x_i
只有一个分支,y_i
在这个分支上。
In conclusion, for any pair [x_i,y_i]
:
- If
degree[x_i]>degree[y_i]
, thenx_i
is the ancestor ofy_i
, and vice versa; - If
degree[x_i]=degree[y_i]
, then there exists multiple ways to build the rooted tree, ancestor can either bex_i
ory_i
.
Code:
class Solution {
public:
int checkWays(vector<vector<int>>& pairs) {
unordered_map<int, unordered_set<int>> adj;
for (auto &p : pairs) {
adj[p[0]].emplace(p[1]);
adj[p[1]].emplace(p[0]);
}
/* 检测是否存在根节点*/
int root = -1;
for (auto &[node, neighbours] : adj) {
if (neighbours.size() == adj.size() - 1) {
root = node;
break;
}
}
if (root == -1) {
return 0;
}
int res = 1;
for (auto &[node, neighbours] : adj) {
if (node == root) {
continue;
}
int currDegree = neighbours.size();
int parent = -1;
int parentDegree = INT_MAX;
/* 根据 degree 的大小找到 node 的父节点 parent */
for (auto &neighbour : neighbours) {
if (adj[neighbour].size() < parentDegree && adj[neighbour].size() >= currDegree) {
parent = neighbour;
parentDegree = adj[neighbour].size();
}
}
if (parent == -1) {
return 0;
}
/* 检测 neighbours 是否是 adj[parent] 的子集 */
for (auto &neighbour : neighbours) {
if (neighbour == parent) {
continue;
}
if (!adj[parent].count(neighbour)) {
return 0;
}
}
if (parentDegree == currDegree) {
res = 2;
}
}
return res;
}
};
leetcode-2045
这道题是用 BFS 求严格次短路径。
- 用邻接表存储地图
- 用两个 vector 分别存储最短路径和次短路径。也可以用
path[i][0], path[i][1]
表示 - 用 queue 装的是 pair,注意
queue<pair<int, int>> q; q.push({a, b});
,其中第一个是当前节点编号,第二个是路径长度 - 最重要的技巧:queue 的 while 循环终止条件不是 empty 而是
path[n][1]==INT_MAX
代码如下:
class Solution {
public:
int secondMinimum(int n, vector<vector<int>>& edges, int time, int change) {
vector<vector<int>> graph(n + 1);
for (auto &e : edges) {
graph[e[0]].push_back(e[1]);
graph[e[1]].push_back(e[0]);
}
// path[i][0] 表示从 1 到 i 的最短路长度,path[i][1] 表示从 1 到 i 的严格次短路长度
vector<vector<int>> path(n + 1, vector<int>(2, INT_MAX));
path[1][0] = 0;
queue<pair<int, int>> q;
q.push({1, 0});
while (path[n][1] == INT_MAX) {
auto [cur, len] = q.front();
q.pop();
for (auto next : graph[cur]) {
if (len + 1 < path[next][0]) {
path[next][0] = len + 1;
q.push({next, len + 1});
} else if (len + 1 > path[next][0] && len + 1 < path[next][1]) {
path[next][1] = len + 1;
q.push({next, len + 1});
}
}
}
int ret = 0;
for (int i = 0; i < path[n][1]; i++) {
if (ret % (2 * change) >= change) {
ret = ret + (2 * change - ret % (2 * change));
}
ret = ret + time;
}
return ret;
}
};