为了更好的阅读体检,可以查看我的算法学习网站
在线评测链接:P1163
题目内容
塔子哥有一家云存储服务提供商,他们家的核心产品是一个可扩展的分布式存储系统。他们的客户使用他们的服务来存储和管理各种数据,包括文档、图片、视频等。由于客户对数据的可靠性和可用性要求非常高,他们需要提供高可用性的存储服务,以确保在任何情况下都能保持服务的可用性。
为了实现高可用性,他们使用了主备模式来管理他们的存储系统。当主节点发生故障时,系统会自动将业务切换到备用节点。为了保证存储系统的稳定性,他们需要及时检测服务状态,并在必要时触发主备切换。
在存储系统中,不同的服务之间存在 依赖关系 ,每个服务最多只会依赖一个其他服务并且保证依赖不成环。例如,某些服务可能需要访问其他服务的数据才能正常工作。因此,当某个服务发生故障时,它所依赖的服务也会受到影响,可能导致更多的服务发生故障。
为了最大限度地减少服务故障对业务的影响,他们需要优先检测对业务影响大的服务,并按照 节点编号升序编排 检测顺序,现在请你帮忙解决一下这个问题。
注意: 如果业务影响相同时,则按节点编号大小升序编排。
输入描述
第一行输入两个整数 n n n 代表业务节点总个数。 ( 1 ≤ n ≤ 100000 ) (1 \leq n \leq 100000) (1≤n≤100000)
接下来一行 n n n个整数,第 i i i 个整数 f i f_i fi 代表$i $ 依赖 f i f_i fi ( i ∈ [ 0 , n − 1 ] ) (i \in [0,n-1]) (i∈[0,n−1]) 。
1.若 f i = − 1 f_i = -1 fi=−1 , 则代表没有节点依赖
2.数据保证 f i ≠ i f_i \neq i fi=i
输出描述
一行 n n n个整数,以空格隔开,代表最终的排序结果.
样例
输入
5
-1 -1 1 2 3
输出
1 2 3 0 4
思路
简单dfs+排序
根据题目内容,我们可以提取3个关键信息:
1.一个点发生故障影响的是它的后继节点
2.后继节点越多,影响越大
3.影响相同的情况下,编号小的排前面
那么一种可行的做法就是:
先对每个点dfs一下,求每个点的后继结点个数。接着对节点按后继结点个数降序排序,相同的按编号升序排序。
类似题目推荐
本质就是考察了一下dfs和自定义排序。较为经典。
LeetCode
CodeFun2000
1.P1224 携程 2023.04.15-春招-第三题-魔法之树
2.P1196 华为实习 2023-04-19-第二题-塔子哥出城
3.P1159. 2022年清华大学(深圳)保研夏令营机试题-第一题-树上计数
4.P1190 华为实习 2023.04.12-第二题-获取最多食物
5.P1044. 拼多多内推笔试-2023.2.22.投喂珍珠
更多请见:知识点分类-训练-深度优先搜索专栏
代码
CPP
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int fa[N]; //节点i的父节点
vector<int> e[N]; //节点i的子节点
int cnt[N]; //节点i的子树中的节点个数
int dfs(int s) //计算s的 子树中的节点个数
{
cnt[s] = 1; //s本身
for(int 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 >> fa[i]; //输入i的父节点
if(fa[i] != -1) { //-1代表i没有父节点,此时i为一棵树的根节点
e[fa[i]].push_back(i); //添加到父节点的子节点集合中
}
}
for(int i = 0 ; i < n ; i++) {
ans[i] = i; //顺便初始化一下排序的值
if(fa[i] == -1) //i为一棵树的根节点,从这个点开始遍历
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]代表最后一次输出回车,前面输出空格分割
}
}
python
from collections import defaultdict
n = int(input())
e = defaultdict(list)
a = list(map(int,input().split()))
# 读入 + 存图
for i in range(n):
if a[i] != -1:
e[a[i]].append(i)
# cnt 子树大小
cnt = [0] * n
# dfs 求子树大小
def dfs(u):
cnt[u] = 1
for v in e[u]:
dfs(v)
cnt[u] += cnt[v]
# 按题目要求排序
res = []
for i in range(n):
if cnt[i] == 0:
dfs(i)
res.append([i , cnt[i]])
res.sort(key = lambda x : (-x[1] , x[0]))
print(" ".join(map(str,[x[0] for x in res])))
Java
import java.lang.reflect.Array;
import java.util.*;
public class Main {
static int N = 100010;
static int n;
static Map<Integer, List<Integer>> graph = new HashMap<>();
static boolean[] visited = new boolean[N];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
for(int i = 0; i < N; i++){
graph.put(i, new ArrayList<>());
}
// 读入,存图
for(int i = 0; i < n; i++){
int temp = in.nextInt();
if(temp != -1){
add(temp, i);
}
}
// dfs 求后继结点个数 , 以二元组形式存储,以便之后的自定义排序
List<int[]> res = new ArrayList<>();
for(int i = 0; i < n; i++){
res.add(new int[]{dfs(i), i});
}
// 自定义排序
Collections.sort(res, new Comparator<int[]>() {
@Override
public int compare(int[] a, int[] b) {
// 根据题解,优先按后继结点个数降序排序
if (a[0] != b[0]) {
return b[0] - a[0];
} else { // 相同情况下 按节点编号升序排序
return Integer.compare(a[1], b[1]);
}
}
});
// 输出答案
for(int[] pair : res){
System.out.print(pair[1] + " ");
}
}
// 计算后继节点个数
private static int dfs(int i){
int count = 0;
for(int j : graph.get(i)){
count += dfs(j);
}
return count+1;
}
private static void add(int a, int b){
graph.get(a).add(b);
}
}
// bt guanam
Go
package main
import (
"fmt"
"sort"
)
const N int = 1e5 + 10
var f []int = make([]int, N)
var n int
type node struct {
id int
sonCnt int
}
func dfs(edges [][]int, x int) int {
if len(edges[x]) == 0 {
return 1
}
res := 1
for _, v := range edges[x] {
res += dfs(edges, v)
}
return res
}
func main() {
//构建有向图 计算每个节点的子节点个数 排序
fmt.Scan(&n)
//有向图的邻接边
edges := make([][]int, n)
for i := range edges {
edges[i] = make([]int, 0)
}
outDegree := make([]int, n) //每个节点的出度
for i := 0; i < n; i++ {
fmt.Scan(&f[i])
if f[i] != -1 {
edges[f[i]] = append(edges[f[i]], i)
outDegree[f[i]]++
}
}
cnt := make([]int, n)
for i := 0; i < n; i++ {
if outDegree[i] == 0 { //如果出度为0 子节点就只算它自己
cnt[i] = 1
} else { //出度不为0 dfs它的所有直接子节点 更新它的总子节点个数
cnt[i] = dfs(edges, i)
}
}
nodes := make([]node, n)
for i := 0; i < n; i++ {
nodes[i].id = i
nodes[i].sonCnt = cnt[i]
}
sort.Slice(nodes, func(i, j int) bool {
if nodes[i].sonCnt != nodes[j].sonCnt { //子节点数量不同时 按照子节点数量降序排列
return nodes[i].sonCnt > nodes[j].sonCnt
}
return nodes[i].id < nodes[j].id //相同时 按照编号升序排列
})
for i := 0; i < n; i++ {
fmt.Printf("%d", nodes[i].id)
if i != n-1 {
fmt.Printf(" ")
}
}
}
// by xchen
Js
// 创建 readline interface 实例
const rl = require('readline').createInterface({input: process.stdin});
// 获取 readline 迭代器
const iter = rl[Symbol.asyncIterator]();
// 定义异步函数,并使用 IIFE 函数立即执行
void async function () {
// 读取第一行输入,n 代表节点个数
const n = parseInt(await readline());
// 读取第二行输入,F 代表以空格分隔的每个节点的父节点编号
const F = (await readline()).split(' ').map(item => parseInt(item));
// 创建 Map,存放每个节点的父节点后继位置的数组
const map = new Map();
// 定义数组存放每个节点所对应的后继结点个数
const count = [];
// 遍历每个节点,判断其父节点是否存在,如果存在,则将该节点下标加入其对应的数组中,否则跳过
for (let i = 0; i < n; i++) {
if (F[i] !== -1) {
if (map.has(F[i])) {
map.set(F[i], [...map.get(F[i]), i]); // 将该节点下标 push 进去其父节点后继位置的数组中
}
else {
map.set(F[i], [i]); // 新建一个数组,存放当前节点下标,指定其为其父节点的后继位置
}
}
}
// 定义深度优先遍历函数,参数分别为 map 和 currV,返回值为当前节点的后继结点个数
const dfs = (map, currV) => {
if (!map.has(currV) || map.get(currV) === 0) { // 当前节点不存在或其没有后继结点,返回 1
return 1;
}
let res = 1; // res 表示当前节点及其后继结点的总个数
const edge = map.get(currV); // 获取当前节点的后继位置数组
for (let V of edge) {
res += dfs(map, V); // 递归计算每个后继结点的后继结点总个数,并将结果加到 res 上
}
return res; // 返回当前节点及其后继结点的总个数
}
// 定义二维数组 comp 存放每个节点的下标和其所对应的后继结点个数
const comp = new Array(n).fill(0).map(item => new Array(2).fill(0));
const res = [];
// 遍历每个节点,计算其所对应的后继结点总数并存入 comp 中
for (let i = 0; i < n; i++) {
comp[i][0] = i; // 第一列存放节点下标
comp[i][1] = dfs(map, i); // 第二列存放节点的后继结点总个数
}
// 对二维数组 comp 进行排序,先按第二列倒序排序,之后按第一列顺序排序
comp.sort((a, b) => {
if (a[1] === b[1]) {
return a[0] - b[0];
}
else {
return b[1] - a[1];
}
})
// 将排序后的节点下标存入 res 数组中
for (let pair of comp) {
res.push(pair[0])
}
// 输出 res 数组中的元素,以空格分隔
console.log(res.join(' '))
}()
题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。