计算云服务DI值-树形DP思想
这道题目是HW某次笔试题目2024.4.17HW-CSDN博客,因为最近在学习树形DP,所以就将该题目重新做了一遍。
题目描述
我们将云服务看做一棵树,每个云服务在发布前尚未解决的问题称为云服务的遗留问题(云服务的遗留问题包含以该云服务为根节点的树上所有节点的问题),DI值(遗留问题缺陷密度)可以作为评估云服务发布的指标,当云服务DI值小于等于阈值时才准许云服务发布,否则视为风险云服务,需要问题整改完成后重新进行发布评估。现有一批云服务树,已给出云服务树各节点的问题数量,请通过计算输出风险云服务的个数。
计算公式:DI值≤5×严重问题数+2×一般问题数,其中每个节点的不同级别问题数量需要将该节点及该节点为根节点的所有子节点的相应级别问题数量求和。
-
输入
第一行输入M和N(M≤100000,N≤1000),使用空格分隔,M表示代表云服务阈值,N表示接下来有N行问题统计数据;
接下来输入一个N∗4的矩阵表,行内使用空格分隔,第一列Ai为服务节点,第二列Bi为Ai的父节点,如果Ai为云服务则无父节点,此时Bi用∗号表示(Ai和Bi取值为字符串,1≤字符串长度≤5,均由小写英文字母或∗号组成),第三列Ci为问题级别(Ci取值为{0,1},0表示严重问题,1表示一般问题),第四列Di为该节点该级别的问题数量(Di≤1000)。
说明:输入保证只出现树的关系,不会出现连通图的情况。 -
输出
风险云服务个数
样例
- 输入
40 12
a * 0 2
a * 1 2
b a 0 3
b a 1 5
c a 1 3
d a 0 1
d a 1 3
e b 0 2
f * 0 8
f * 1 10
g f 1 2
h * 0 4
- 输出
2
分析
在另一篇博客中通过构建N叉树2024.4.17HW-CSDN博客,然后将N叉树进行层次遍历的方法。当时谈到本题也可以采用树形DP或者并查集的方法。
关于并查集,本题主要是采用并查集对树进行路径压缩,使得每棵树上的根节点的高度为1(某节点的深度是指从根节点到该节点的最长简单路径边的条数,高度是指从该节点到叶子节点的最长简单路径边的条数)
关于树形DP,本题可以定义一个二维dp
- dp[i][0]表示从i节点到其所有子节点的问题类型为0的数量
- dp[i][1]表示从i节点到其所有子节点的问题类型为1的数量
- 不过为了方便起见,本题可以直接采用两个变量来存储这两个问题的数量
- 关于树形DP的大多数题目都是采用dfs遍历,先遍历子节点,再遍历根节点,也就是后续遍历。不过,由于我们只是需要把每棵树上的所有同类问题树相加,因此依旧可以采用层次遍历
- 本题的关键点依旧在于构建树,不过和本人另一篇博客2024.4.17HW-CSDN博客不同的是,本题采用了邻接表的形式存储树。用邻接矩阵存储树确实是比直接构建树简单了许多。
代码
完整代码如下,关于代码中有什么错误或者看不懂的大家都可以私聊我探讨。
#include <iostream>
#include <map>
#include <unordered_set>
#include <vector>
#include <string>
#include <set>
#include <queue>
using namespace std;
void dfs(map<string, set<string>> matric, map<string, int> questions0, map<string, int> questions1, string father, int& count0, int& count1) {
for (auto& setMatric : matric[father]) {
dfs(matric, questions0, questions1, setMatric, count0, count1);
if (questions0.count(setMatric))count0 += questions0[setMatric];
if (questions1.count(setMatric))count1 += questions1[setMatric];
}
}
int main() {
int M, N;
cin >> M >> N;
map<string, int> questions0;//用来记录节点的问题类型0的问题数量
map<string, int> questions1;//用来记录节点的问题类型1的问题数量
map<string, set<string>> matric;//用邻接矩阵的形式存储数
set<string> father;//用来记录根节点,因为本题根节点可能不止一个
for (int i = 0; i < N; i++) {
string node_name, parent_name;
int model, price;
cin >> node_name >> parent_name >> model >> price;
//将问题类型数量与和节点名对应
if (model == 0) {
questions0[node_name] = price;
}
else {
questions1[node_name] = price;
}
//这里有一个bug,就是如果当前节点是根节点,且该根节点没有子节点,那么该节点是不会在matric关联矩阵中出现的
//不过这里用father记录了根节点,因此最后所有树(包括该树只有唯一的一个根节点的情况)都可以遍历到
if (parent_name == "*") {
//当前节点是根节点
father.insert(node_name);
}
else {
//当前节点不是根节点
matric[parent_name].insert(node_name);
}
}
int riskServerCount = 0;//记录风险服务器的个数
for (auto& f : father) {
//注意这里要将model0_count和model1_count初始化为questions0[f]和questions1[f]
//因为我们在dfs的时候并没有遍历根节点
int model0_count = questions0[f];//严重问题数
int model1_count = questions1[f];//一般问题数
dfs(matric, questions0, questions1, f, model0_count, model1_count);
cout << model0_count << " " << model1_count << endl;
if (M < (5 * model0_count + 2 * model1_count)) {
riskServerCount++;
}
cout << endl;
}
cout << "riskServerCount: " << riskServerCount << endl;
return 0;
}