题目传送门

一、二叉树+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;
}