一、题目链接
P1364 医院设置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
二、题目简介
设有一棵二叉树,如图:
其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为 11。如上图中,若医院建在 11 处,则距离和 =4+12+2×20+2×40=136=4+12+2×20+2×40=136;若医院建在 33 处,则距离和 =4×2+13+20+40=81=4×2+13+20+40=81。
输入格式
第一行一个整数 n,表示树的结点数。
接下来的 n 行每行描述了一个结点的状况,包含三个整数 w,u,v,其中 w 为居民人口数,u 为左链接(为 00 表示无链接),v 为右链接(为 00 表示无链接)。
输出格式
一个整数,表示最小距离和。
输入输出样例
输入
5 13 2 3 4 0 0 12 4 5 20 0 0 40 0 0
输出
81
说明/提示
数据规模与约定
对于 100%100% 的数据,保证 1≤n≤1001≤n≤100,0≤u,v≤n0≤u,v≤n,1≤w≤1051≤w≤105。
三、涉及知识点
DFS,深度优先搜索,二叉树的建立
四、算法分析
做这个题之前首先要手搓二叉树,像图,二叉树,这两种在STL或者java中的库函数中是没有的,不能直接调用,所以需要手搓。
- 首先建立一个类,用于创建二叉树的结点。
- 在主函数中将数据输入到二叉树中,,在输入之前需要创建一个数组用于放二叉树的结点。
- 再创建一个vis数组用于标记当前是否被访问,用0来标记未被访问,1标记被访问过。
- ans取最大的int值,再用min取较小的值。
- 最后再打印结果
详细解答请看源码讲解
五、源码讲解
输入部分
n = sc.nextInt();
for (int i = 1; i <= n; i++) {
t[i] = new TreeNode();
t[i].val = sc.nextInt();
t[i].left = sc.nextInt();
t[i].right = sc.nextInt();
vis[i] = 0;
}
t[1].father = 0;
for (int i = 1; i <= n; i++) {
if (t[i].left != 0)
t[t[i].left].father = i;//左节点的父节点
if (t[i].right != 0)
t[t[i].right].father = i;//右节点的父节点
}
- 首先输入n,代表你所输入的二叉树节点的数量,接下来n行第一个数据是二叉树的节点值,第二个数据是左边所指向的第几个结点,第三个数据是右边所指向的第几个结点。
- 那么难点来了,父节点怎样确定呢?第一个节点的父节点自然是0了,也就是空,
- 当你将所有节点的左右节点输入完毕以后,是不是当前节点的左节点的父节点就是当前结点。不过在此之前需要判断当前节点有没有左节点或者右节点。
dfs核心代码
public static int dfs(int step, int pos) {
if (vis[step] == 1 || step == 0) {
return 0;
}
vis[step] = 1;
int fa = t[step].father, l = t[step].left, r = t[step].right;
int x = dfs(l, pos + 1) + dfs(r, pos + 1) + dfs(fa, pos + 1) + t[step].val * pos;
vis[step] = 0;
return x;
}
- 定义一个step代表当前第几个结点,pos代表距离深度
- 如果这个节点搜索过了,就将vis中当前数组标记为1,那么在下一次的就可以自动返回不再搜索。
- 在main函数中有for循环,首先从第一个节点值开始,深度优先搜索,不理解的可以简单理解为递归向一个方向搜索,比如这道题的源码就是先向左边搜索,一直递归到左边为空,之后在返回,在向右,一直搜索到右边为空。
- 下面是一个i为1时的搜索图,搜索路线可以简化为 1->2->4->1->3->4->3->5 源码中的return使他们返回到原来节点,比如,搜索到2时,无法搜索,return到1并且返回4,之后再向右搜索。
- 而dfs(fa,pos+1)就是为了防止开始结点不是头结点而使用的,比如在开始搜索的头结点为2时,左右两边都无法搜索,这时就需要向上搜索了,之后再向左,向右搜索。
详细代码
import java.util.Scanner;
class TreeNode {
int val;
int left;
int right;
int father;
}
public class Main{
static int n, sum, ans = Integer.MAX_VALUE;
static TreeNode[] t = new TreeNode[106];
static int[] vis = new int[105];
//第i个结点,深度
public static int dfs(int step, int pos) {
if (vis[step] == 1 || step == 0) {
return 0;
}
vis[step] = 1;
int fa = t[step].father, l = t[step].left, r = t[step].right;
int x = dfs(l, pos + 1) + dfs(r, pos + 1) + dfs(fa, pos + 1) + t[step].val * pos;
vis[step] = 0;
return x;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 1; i <= n; i++) {
t[i] = new TreeNode();
t[i].val = sc.nextInt();
t[i].left = sc.nextInt();
t[i].right = sc.nextInt();
vis[i] = 0;
}
t[1].father = 0;
for (int i = 1; i <= n; i++) {
if (t[i].left != 0)
t[t[i].left].father = i;//左节点的父节点
if (t[i].right != 0)
t[t[i].right].father = i;//右节点的父节点
}
for (int i = 1; i <= n; i++) {
if (vis[i] != 1) {//未被访问过
ans = Math.min(ans, dfs(i, 0));
}
}
System.out.println(ans);
}
}
在调用dfs时在循环中也是因为拥有多个节点,每个节点都需要作为头节点来搜索,并在这里找到做小值,并输出。
C++代码,C++如果有不懂的请私信
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
int left;
int right;
int father;
}t[105];
int n, sum, ans = INT_MAX,vis[105];
void dfs(int step, int pos) {
sum += step * t[pos].val;
int fa = t[pos].father, l = t[pos].left, r = t[pos].right;
if (fa && !vis[fa]) {
vis[fa] = 1;
dfs(step + 1, fa);
}
if (l && !vis[l]) {
vis[l] = 1;
dfs(step + 1, l);
}
if (r &&! vis[r] ) {
vis[r] = 1;
dfs(step + 1, r);
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> t[i].val>> t[i].left >> t[i].right;
t[t[i].left].father = i;
t[t[i].right].father = i;
}
for (int i = 1; i <= n; i++) {
sum = 0;
memset(vis,0,sizeof(vis));
vis[i] = 1;
dfs(0, i);
ans = min(ans,sum);
}
cout << ans << endl;
return 0;
}
六、FAQ
请各位大佬指正,共勉!