洛谷P1364医院设置 java和C++

一、题目链接

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中的库函数中是没有的,不能直接调用,所以需要手搓。

  1. 首先建立一个类,用于创建二叉树的结点。
  2. 在主函数中将数据输入到二叉树中,,在输入之前需要创建一个数组用于放二叉树的结点。
  3. 再创建一个vis数组用于标记当前是否被访问,用0来标记未被访问,1标记被访问过。
  4. ans取最大的int值,再用min取较小的值。
  5. 最后再打印结果

详细解答请看源码讲解

五、源码讲解

输入部分

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;//右节点的父节点
		}
  1. 首先输入n,代表你所输入的二叉树节点的数量,接下来n行第一个数据是二叉树的节点值,第二个数据是左边所指向的第几个结点,第三个数据是右边所指向的第几个结点。
  2. 那么难点来了,父节点怎样确定呢?第一个节点的父节点自然是0了,也就是空,
  3. 当你将所有节点的左右节点输入完毕以后,是不是当前节点的左节点的父节点就是当前结点。不过在此之前需要判断当前节点有没有左节点或者右节点。

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;
	}
  1. 定义一个step代表当前第几个结点,pos代表距离深度
  2. 如果这个节点搜索过了,就将vis中当前数组标记为1,那么在下一次的就可以自动返回不再搜索。
  3. 在main函数中有for循环,首先从第一个节点值开始,深度优先搜索,不理解的可以简单理解为递归向一个方向搜索,比如这道题的源码就是先向左边搜索,一直递归到左边为空,之后在返回,在向右,一直搜索到右边为空。
  4. 下面是一个i为1时的搜索图,搜索路线可以简化为    1->2->4->1->3->4->3->5  源码中的return使他们返回到原来节点,比如,搜索到2时,无法搜索,return到1并且返回4,之后再向右搜索。
  5. 而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

请各位大佬指正,共勉!

  • 35
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值