为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1353
题目描述
一个文件目录的数据格式为: 目录 i d id id,本目录中文件大小,(子目录 i d id id列表)。
其中目录 i d id id全局唯一,取值范围[ 1 , 200 1,200 1,200],本目录中文件大小范围[ 1 , 1000 1,1000 1,1000],子目录 i d id id列表个数[ 0 , 10 0,10 0,10]例如 : 1 1 1 20 20 20( 2 , 3 2,3 2,3)表示目录 1 1 1中文件总大小是 20 20 20,有两人子目录, i d id id分别是 2 2 2和 3 3 3
现在输入一个文件系统中所有目录信息,以及待查询的目录 i d id id,返回这个目录和及该目录所有子目录的大小之和。
输入描述
第一行为两个数字 M M M, N N N,分别表示目录的个数和待查询的目录 i d id id,
-
1
≤
M
≤
100
1\leq M \leq100
1≤M≤100
-
1
≤
N
≤
200
1\leq N\leq200
1≤N≤200
接下来 M M M行,每行为 1 1 1个目录的数据:
目录id 本目录中文件大小(子目录id列表)
子目录列表中的子目录 i d id id以逗号分隔。
输出描述
待查询目录及其子目录的大小之和。
样例
输入
3 1
3 15 ()
1 20 (2)
2 10 (3)
输出
45
说明
目录 1 1 1大小为 20 20 20,包含一个子目录 2 2 2(大小为 10 10 10),子目录 2 2 2包含一个子目录 3 3 3(大小为 15 15 15),总的大小为 20 + 10 + 15 = 45 20+10+15=45 20+10+15=45
输入
4 2
4 20 ()
5 30 ()
2 10 (4,5)
1 40 ()
输出
60
说明
目录 2 2 2包含 2 2 2个子目录 4 4 4和 5 5 5,总的大小为 10 + 20 + 30 = 60 10+20+30=60 10+20+30=60
思路:树的遍历 dfs
这是一道典型的建图加dfs遍历的问题。
当前目录的大小是确定的,当前目录的子目录是有很多级的。
而第 i 级子目录必然是某个第 i-1 级子目录的 1 级子目录。
故求当前目录的所有子目录的大小,等于求其所有 1 级子目录的大小,以及所有 1 级子目录的子目录的大小。
这样就是一个子问题了,故可以递归解决。
d f s ( u ) dfs(u) dfs(u)表示目录 u u u的大小以及目录 u u u的所有子目录的大小之和。
d f s ( u ) = w [ u ] + ∑ v i s s o n o f u d f s ( v ) dfs(u) = w[u] + \sum\limits_{v\ is\ son\ of u} dfs(v) dfs(u)=w[u]+v is son ofu∑dfs(v)
这里的 v v v是 u u u的儿子,即 v v v是 u u u的一级子目录。
时间复杂度: O ( n ) O(n) O(n)
类似题目推荐
Codefun2000
树形
- 蚂蚁 P1154. 2023.04.04-研发岗-第二题-子树
- 小红书 P1177. 2023.04.09-春招-第一题-拆分树
- 清华大学 P1159. 2022年清华大学(深圳)保研夏令营机试题-第一题-树上计数
LeetCode
拓扑排序
代码
C++
#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
int main()
{
int m, idx;
cin >> m >> idx;
// g[i] 表示目录 i 的所有一级子目录
vector<vector<int>> g(N + 1);
// w[i] 表示目录 i 的文件大小
vector<int> w(N + 1);
for (int i = 0; i < m; ++i) {
int x;
cin >> x;
cin >> w[x];
string s;
cin >> s;
int len = s.size();
for (int j = 0; j < len; ++j) {
// 如果不是数字,说明是分隔符
if (!isdigit(s[j])) continue;
int t = 0;
int k = j;
while (k < len && isdigit(s[k])) {
t = t * 10 + (s[k] - '0');
k += 1;
}
// t 是其中一个子目录,如此由 x 指向 t ,构造出一个目录树
g[x].emplace_back(t);
j = k - 1;
}
}
function<int(int)> dfs = [&](int u) {
// 初始化为当前目录的大小
int res = w[u];
for (int v: g[u]) {
// 获取所有子目录的大小
res += dfs(v);
}
return res;
};
// 从查询的目录 idx 开始,求出这个目录和其所有子目录的大小
cout << dfs(idx) << "\n";
return 0;
}
python
N = 1000
def dfs(u, g, w):
# 初始化为当前目录的大小
res = w[u]
for v in g[u]:
# 获取所有子目录的大小
res += dfs(v, g, w)
return res
m, idx = map(int, input().split())
# g[i] 表示目录 i 的所有一级子目录
g = [[] for _ in range(N + 1)]
# w[i] 表示目录 i 的文件大小
w = [0] * (N + 1)
for i in range(m):
x, wx, s = input().split()
x, wx = int(x), int(wx)
w[x] = wx
lenS = len(s)
j = 0
while j < lenS:
# 如果不是数字,说明是分隔符
if not s[j].isdigit():
j += 1
continue
t = 0
k = j
while k < lenS and s[k].isdigit():
t = t * 10 + int(s[k])
k += 1
# t 是其中一个子目录,如此由 x 指向 t ,构造出一个目录树
g[x].append(t)
j = k
# 从查询的目录 idx 开始,求出这个目录和其所有子目录的大小
print(dfs(idx, g, w))
Java
import java.util.*;
public class Main {
static final int N = 1000;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int idx = sc.nextInt();
// g[i] 表示目录 i 的所有一级子目录
List<List<Integer>> g = new ArrayList<>();
for (int i = 0; i <= N; i++) {
g.add(new ArrayList<>());
}
// w[i] 表示目录 i 的文件大小
int[] w = new int[N + 1];
for (int i = 0; i < m; ++i) {
int x = sc.nextInt();
w[x] = sc.nextInt();
String s = sc.next();
int len = s.length();
for (int j = 0; j < len; ++j) {
// 如果不是数字,说明是分隔符
if (!Character.isDigit(s.charAt(j))) continue;
int t = 0;
int k = j;
while (k < len && Character.isDigit(s.charAt(k))) {
t = t * 10 + (s.charAt(k) - '0');
k += 1;
}
// t 是其中一个子目录,如此由 x 指向 t ,构造出一个目录树
g.get(x).add(t);
j = k - 1;
}
}
// 从查询的目录 idx 开始,求出这个目录和其所有子目录的大小
System.out.println(dfs(idx, g, w));
}
static int dfs(int u, List<List<Integer>> g, int[] w) {
// 初始化为当前目录的大小
int res = w[u];
for (int v : g.get(u)) {
// 获取所有子目录的大小
res += dfs(v, g, w);
}
return res;
}
}
Go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
const N = 1000
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
scanner.Scan()
m, _ := strconv.Atoi(scanner.Text())
scanner.Scan()
idx, _ := strconv.Atoi(scanner.Text())
// g[i] 表示目录 i 的所有一级子目录
g := make([][]int, N+1)
// w[i] 表示目录 i 的文件大小
w := make([]int, N+1)
for i := 0; i < m; i++ {
scanner.Scan()
x, _ := strconv.Atoi(scanner.Text())
scanner.Scan()
w[x], _ = strconv.Atoi(scanner.Text())
scanner.Scan()
s := scanner.Text()
lenS := len(s)
for j := 0; j < lenS; j++ {
// 如果不是数字,说明是分隔符
if !isDigit(s[j]) {
continue
}
t := 0
k := j
for k < lenS && isDigit(s[k]) {
t = t*10 + int(s[k]-'0')
k += 1
}
// t 是其中一个子目录,如此由 x 指向 t ,构造出一个目录树
g[x] = append(g[x], t)
j = k - 1
}
}
// 从查询的目录 idx 开始,求出这个目录和其所有子目录的大小
fmt.Println(dfs(idx, g, w))
}
func dfs(u int, g [][]int, w []int) int {
// 初始化为当前目录的大小
res := w[u]
for _, v := range g[u] {
// 获取所有子目录的大小
res += dfs(v, g, w)
}
return res
}
func isDigit(c byte) bool {
return c >= '0' && c <= '9'
}
Js
process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
input += data;
return;
});
process.stdin.on('end', () => {
const lines = input.trim().split('\n');
const [m, idx] = lines[0].split(' ').map(Number);
// g[i] 表示目录 i 的所有一级子目录
const g = Array.from({ length: N + 1 }, () => []);
// w[i] 表示目录 i 的文件大小
const w = Array(N + 1).fill(0);
for (let i = 1; i <= m; i++) {
const [x, wx, s] = lines[i].split(' ');
w[x] = Number(wx);
const lenS = s.length;
for (let j = 0; j < lenS; j++) {
// 如果不是数字,说明是分隔符
if (!isDigit(s[j])) {
continue;
}
let t = 0;
let k = j;
while (k < lenS && isDigit(s[k])) {
t = t * 10 + (s[k] - '0');
k += 1;
}
// t 是其中一个子目录,如此由 x 指向 t ,构造出一个目录树
g[x].push(t);
j = k - 1;
}
}
// 从查询的目录 idx 开始,求出这个目录和其所有子目录的大小
console.log(dfs(idx, g, w));
});
const N = 1000;
function dfs(u, g, w) {
// 初始化为当前目录的大小
let res = w[u];
for (const v of g[u]) {
// 获取所有子目录的大小
res += dfs(v, g, w);
}
return res;
}
function isDigit(c) {
return c >= '0' && c <= '9';
}