✊✊✊🌈大家好!本篇文章将较详细介绍两道考点为dfs的华为机试题目。代码语言为:C++代码😇。
2022.9.23 T1 最佳检测顺序
🔒1、题目:
塔子哥有一家云存储服务提供商,他们家的核心产品是一个可扩展的分布式存储系统。他们的客户使用他们的服务来存储和管理各种数据,包括文档、图片、视频等。由于客户对数据的可靠性和可用性要求非常高,他们需要提供高可用性的存储服务,以确保在任何情况下都能保持服务的可用性。
为了实现高可用性,他们使用了主备模式来管理他们的存储系统。当主节点发生故障时,系统会自动将业务切换到备用节点。为了保证存储系统的稳定性,他们需要及时检测服务状态,并在必要时触发主备切换。
在存储系统中,不同的服务之间存在 依赖关系 ,每个服务最多只会依赖一个其他服务并且保证依赖不成环。例如,某些服务可能需要访问其他服务的数据才能正常工作。因此,当某个服务发生故障时,它所依赖的服务也会受到影响,可能导致更多的服务发生故障。
为了最大限度地减少服务故障对业务的影响,他们需要优先检测对业务影响大的服务,并按照 节点编号升序编排 检测顺序,现在请你帮忙解决一下这个问题。
注意: 如果业务影响相同时,则按节点编号大小升序编排。
🌲 示例 🌲:
输入
5
-1 -1 1 2 3
输出
1 2 3 0 4
解释:服务0、1没有依赖,2依赖1,3依赖2,4依赖3。此时2、3、4业务影响都是1,0、1为0。按序号排为1 2 3 0 4。
☀️2、思路:
简单dfs+排序
根据题目内容,我们可以提取3个关键信息:
1.一个点发生故障影响的是它的后继节点
2.后继节点越多,影响越大
3.影响相同的情况下,编号小的排前面
一种可行的做法就是:
先对每个点dfs一下,求每个点的后继结点个数。接着对节点按后继结点个数降序排序,相同的按编号升序排序。
细节
1.利用vector e[N]存储节点i的子节点,后续dfs时可以遍历e[N]。
2.最初将ans[i]初始化为i,是为了后面更好输出。利用cmp排序对ans数组进行排序,之后直接按序输出ans数组即为答案。
int ans[N];
bool cmp(int& x, int& y) { //重写排序函数,按照子树中节点个数排序,相等时按大小排序
if (cnt[x] == cnt[y]) return x < y;
return cnt[x] > cnt[y];
}
for (int i = 0; i < n; i++) {
ans[i] = i;
if (father[i] == -1)
dfs(i);
}
sort(ans, ans + n, cmp);
🔑3、代码:
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int father[N]; //节点i的父节点
vector<int> e[N]; //节点i的子节点
int cnt[N]; //节点i的子树中的节点个数
int dfs(int s) //计算s的 子树中的节点个数
{
cnt[s] = 1; //s本身
for (auto g : e[s]) //遍历s的每个子节点
{
cnt[s] += dfs(g); //递归计算每个子节点的子树的节点个数,然后加到s上
}
return cnt[s]; //返回s的子树的节点个数
}
int ans[N];
bool cmp(int& x, int& y) { //重写排序函数,按照子树中节点个数排序,相等时按大小排序
if (cnt[x] == cnt[y]) return x < y;
return cnt[x] > cnt[y];
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> father[i];
if (father[i] != -1) {
e[father[i]].emplace_back(i);
}
}
for (int i = 0; i < n; i++) {
ans[i] = i;
if (father[i] == -1)
dfs(i);
}
sort(ans, ans + n, cmp);
for (int i = 0; i < n; i++) {
cout << ans[i] << " \n"[i + 1 == n]; //输出," \n"[i+1==n]代表最后一次输出回车,前面输出空格分割
}
return 0;
}
2022.9.23 T1 最佳检测顺序
🔒1、题目:
塔子哥居住在数据结构之城,如果将这个城市的路口看做点,两个路口之间的路看做边,那么该城市的道路能够构成一棵由市中心路口向城市四周生长的树,树的叶子节点即是出城口。
塔子哥今天想要出城办事,但不巧的是,有几个路口堵车了,塔子哥无法从一个正常的路口前往堵车的路口。假定塔子哥从一个正常的路口出发,请问塔子哥能否顺利出城(到达出城口)?如果可以,请帮塔子哥找到最省油的路径(经过路口最少的路径),否则请输出“
N
U
L
L
NULL
NULL”。
🌲 示例 1 🌲:
输入:
4
3
0 1
0 2
0 3
2
2
3
输出:
0->1
说明:
🌲 示例 2 🌲:
输入:
7
6
0 1
0 3
1 2
3 4
1 5
5 6
1
4
输出:
0->1->2
说明:
☀️2、思路:
题意化简:给定一棵树,其中有些点无法访问。需要找一条从根节点到叶子结点的路径,要求满足长度最短且字典序最小。
直接dfs,在这个过程中存储好已访问的路径。然后遇到叶子结点就用当前路径更新答案路径。
这样做的复杂度是 O ( n + ∑ d e p t h ( l e a f n o d e ) ) O(n+∑depth(leafnode)) O(n+∑depth(leafnode)),其中第一项来自于dfs的开销,第二项来自于 <在叶子结点的记录答案>的开销。
🔑3、代码:
#include<bits/stdc++.h>
#include <vector>
using namespace std;
int n, m;
vector<vector<int>> edges;
vector<int> blocks;
vector<int> path; // 当前路径
vector<int> res; // 答案路径
int tmp = INT_MAX;
vector<bool> used; // 标记是否被访问.
bool judge = false;
// idx 为当前所在点 , num 为深度
void dfs(int idx, int num) {
// 到叶子节点,更新答案
if (edges[idx].size() == 0) {
if (num < tmp) {
tmp = num;
res = path;
}
judge = true;
}
//对同一层进行排序。这样保证了最先遇到的最短的答案也是字典序最小的
sort(edges[idx].begin(), edges[idx].end());
// 递归
for (auto& a : edges[idx]) {
if (blocks[a] == 0 && used[a] == false) {
used[a] = true;
path.push_back(a);
dfs(a, num + 1);
path.pop_back();
used[a] = false;
}
}
}
int main() {
// 读入
cin >> n >> m;
edges.resize(n + 1);
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
edges[a].push_back(b);
}
// 标记 不能访问的点
int k;
cin >> k;
blocks.resize(n + 1, 0);
for (int i = 0; i < k; i++) {
int k1;
cin >> k1;
blocks[k1] = 1;
}
used.resize(n + 1, false);
path.push_back(0);
used[0] = true;
// dfs
dfs(0, 1);
// 输出
if (judge) {
for (int i = 0; i < res.size(); i++) {
cout << res[i];
if (i != res.size() - 1) {
cout << "->";
}
}
}
else cout << "NULL" << endl;
return 0;
}