题目传送门
一、二叉树+bfs解法
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 210;
bool st[N]; //是不是走过了
int n, ans = 0x3f3f3f3f;
struct Node {
int left; //左儿子
int right; //右儿子
int value; //此结点的人数
int parent; //父亲是哪结点
} nodes[N];
//广度优先搜索
int bfs(int u) {
//既然是bfs,当然需要把根结点先入队列
queue<PII> q;
q.push({u, 0});
//以u为源点的路径权值和
int sum = 0;
//开始广度优先搜索
while (!q.empty()) {
PII c = q.front();
q.pop();
//自己产生的权值
sum += c.second * nodes[c.first].value;
//标识已使用
st[c.first] = true;
//左子树
if (nodes[c.first].left && !st[nodes[c.first].left]) q.push({nodes[c.first].left, c.second + 1});
//右子树
if (nodes[c.first].right && !st[nodes[c.first].right]) q.push({nodes[c.first].right, c.second + 1});
//父子树
if (nodes[c.first].parent && !st[nodes[c.first].parent]) q.push({nodes[c.first].parent, c.second + 1});
}
return sum;
}
int main() {
//读入数据
cin >> n;
for (int i = 1; i <= n; i++) {
//读入权值,左儿子,右儿子
cin >> nodes[i].value >> nodes[i].left >> nodes[i].right;
//记录左右儿子的父亲节点
nodes[nodes[i].left].parent = i; //为了找到父亲
nodes[nodes[i].right].parent = i;//为了找到父亲
}
//遍历每个结点作为医院部署地
for (int i = 1; i <= n; i++) {
//每次出发前,注意要清空状态数组,防止状态记录错误
memset(st, 0, sizeof st);
//每个结点作为医院部署地,都会产生一个距离和,取最小值
ans = min(ans, bfs(i));
}
cout << ans << endl;
return 0;
}
二、二叉树+dfs解法
#include <bits/stdc++.h>
using namespace std;
const int N = 210;
bool st[N]; //是不是走过了
int n, ans = 0x3f3f3f3f;
struct Node {
int num; //几号结点
int left; //左儿子
int right; //右儿子
int value; //此结点的人数
int parent; //父亲是哪结点
} nodes[N];
/**
* 功能:计算以u为起点,step步数下的权值
* @param u
* @param step
* @return
*/
int dfs(int u, int step) {
//不加st数组的话,会Memory Limit Exceed 超出内存限制
//如果不加st数组的话,就会发生:它调了它的父亲,它的父亲又调了它,来来回回,不死不休~,这样看来,状态数组是多么的重要啊~
//问题1:什么时间退出递归?nodes[u].num==0表示找到了一个终端结点,就是叶子
if (nodes[u].num == 0 || st[u] == true) return 0;
//标识为已使用过
st[u] = true;
//问题2:如何计算整体的权值
//这里就是个深度优先搜索,三个侦察兵,左子树,右子树,父子树的距离和.
//分别对三个方向的侦查兵说:以你为根的子树,距离我step+1这么远,你负责统计一下你子树的距离和!
//[u,step]的权值=dfs(左儿子)+dfs(右儿子)+自己的权值+dfs(父亲)
return dfs(nodes[u].left, step + 1) + dfs(nodes[u].right, step + 1) +
dfs(nodes[u].parent, step + 1) + nodes[u].value * step;
}
int main() {
//读入数据
cin >> n;
for (int i = 1; i <= n; i++) {
//读入权值,左儿子,右儿子
cin >> nodes[i].value >> nodes[i].left >> nodes[i].right;
//记录结点号
nodes[i].num = i;
//记录左右儿子的父亲节点
nodes[nodes[i].left].parent = i; //为了找到父亲
nodes[nodes[i].right].parent = i;//为了找到父亲
}
//遍历每个结点作为医院部署地
for (int i = 1; i <= n; i++) {
//每次出发前,注意要清空状态数组,防止状态记录错误
memset(st, 0, sizeof st);
//每个结点作为医院部署地,都会产生一个距离和,取最小值
ans = min(ans, dfs(i, 0));
}
cout << ans << endl;
return 0;
}
三、邻接矩阵+bfs解法
#include <bits/stdc++.h>
using namespace std;
const int N = 210;
bool d[N][N]; //这里n小我就直接邻接矩阵了,如果用邻接表还能快点
bool st[N]; //是不是使用过了,这个是必须要有了,否则不就死循环了吗
int p[N]; //权值数组
int n; //n个医院
int ans = 0x3f3f3f3f; //答案
struct Node {
int num; //几号结点
int step;//到这个结点走了多少步了
};
//bfs找当前点x为医院设置点时的总距离
int bfs(int x) {
//每次计算前需要清空一下使用过的标识数组
memset(st, 0, sizeof(st));
//队列
queue<Node> q;
st[x] = true; //使用过了
q.push({x, 0});//放入队列
int sum = 0;//本次运算的权值和
while (!q.empty()) {
Node node = q.front();
q.pop();
for (int i = 1; i <= n; i++)
//如果两个结点之间是联通的,并且没有走过
if (d[node.num][i] && !st[i]) {
//准备继续探索
Node next = {i, node.step + 1};
sum += p[i] * next.step; //权值在增加
st[i] = true; //标识使用过了
q.push(next); //放入队列
}
}
return sum;
}
int main() {
//读入数据到邻接矩阵
cin >> n;
for (int i = 1; i <= n; i++) {
int l, r;
cin >> p[i] >> l >> r;
if (l) d[i][l] = d[l][i] = true;//为0表示无链接,双向建图
if (r) d[i][r] = d[r][i] = true;//为0表示无链接,双向建图
}
//遍历每个结点作为医院部署地
for (int i = 1; i <= n; i++) ans = min(ans, bfs(i));
cout << ans << endl;
return 0;
}
四、邻接矩阵+floyd解法
#include <bits/stdc++.h>
using namespace std;
const int N = 210;
const int INF = 0x3f3f3f3f;
int minn = INF;
int n, m, k;
int d[N][N]; //邻接矩阵存储
int p[N];
// 算法结束后,d[a][b]表示a到b的最短距离,k->i->j
void floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main() {
cin >> n;
//floyd初始化
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (i == j) d[i][j] = 0;
else d[i][j] = INF;
//读入数据
for (int i = 1; i <= n; i++) {
int a, b;
cin >> p[i] >> a >> b;
if (a) d[a][i] = d[i][a] = 1;
if (b) d[b][i] = d[i][b] = 1;
}
//弗洛依德算法
floyd();
//模拟
for (int i = 1; i <= n; i++) {//模拟1-n的每一个结点,尝试将医院放在这里
int s = 0; //看看取得的”所有居民所走的路程之和“是多少,取最小值
for (int j = 1; j <= n; j++) s += d[i][j] * p[j];
//取最小值
minn = min(minn, s);
}
cout << minn << endl;
return 0;
}