道路建设java

这是一个有趣的题目,我们来详细分析题目的解法

描述

H 国有 n 座城市和 n-1 条无向道路,保证每两座城市都可以通过道路互相到达。现在 H 国要开始施工,施工分若干个阶段,第 i 个阶段会建设无向道路 (x,y)

,当且仅当存在一个数 z,满足 x ≠ z, x ≠ y, z ≠ y,且在第 i-1 个阶段后,存在无向道路 (x,z), (z,y).

现在 H 国的国王想知道,在几个阶段后,每两个不同的城市之间都有一条无向道路.

输入
第一行一个正整数 n
接下来 n-1 行,每行两个正整数 (x,y),描述一开始的一条无向道路 (x,y)
1 ≤ n ≤ 105

输出
输出最少几个阶段后,每两个不同的城市之间都有一条无向道路.

样例输入:

3

1 2

2 3

样例输出:

1

从题目的含义上看,就是找出最少的步数,每一步,将树中两点间最短距离为2的点(每条边的长度看做1),添加一条边将两点的最短距离变为1.很明显通过一步一步的操作,最终所有两点间的距离都会变成1,也就是国王希望达到的效果。
最初分析这个题目可能还无法找到合适的切入点,但是你换一个角度来看问题: 国王希望的效果,就是希望图的任意两点间的最短距离为1,而且通过一步一步的操作,原来的点之间的距离是一定会缩短的,每做一步操作,两个点之间的距离都会缩小一点,那我们可以先找到树中距离最大的两个点,然后看多少步操作能将这两个点的距离减为1,这就是我们需要求解的最小操作步数。而且每做一步操作,两点之间的距离会缩短一半(奇数距离和偶数距离有一点区别,需要注意)。
这里写图片描述
所以上述问题的求解方案的关键步骤就转化成了,求解树中的任意两点间的最大距离。其实这个问题就是求树的直径,关于如何求树的直径后面的文章再详细介绍。接下来是代码实现。

import java.util.*;

class TreeNode {
    public int node_id = -1; //树节点的ID
    public ArrayList<TreeNode> friends = new ArrayList<TreeNode>(); //兄弟节点
    public int  dist = -1; //用于后文中求解最大距离
};
/**
* 求解离树tree_node_id距离最远的点
*/
public int getMaxDistTreeNode(TreeNode[] tree_node_list, int tree_node_id) {
    for(int i = 1; i < tree_node_list.length; i++) {
        tree_node_list[i].dist = -1; // 代表没有被操作过
    }
    //计算每个树节点到tree_node_id节点的距离
    tree_node_list[tree_node_id].dist = 0;
    LinkedList<Integer> handle_list = new LinkedList<Integer>();
    handle_list.add(tree_node_id);
    while(handle_list.size() != 0) {
        int handle_tree_id = handle_list.pollFirst();
        TreeNode handle_node = tree_node_list[handle_tree_id];
        for(TreeNode friend_node : handle_node.friends) {
            if(friend_node.dist != -1) continue;
            friend_node.dist = handle_node.dist + 1;
            handle_list.addLast(friend_node.node_id);
        } 
    }
    //找到离tree_node_id点最远的点
    TreeNode max_node = tree_node_list[1];
    for(int i = 1; i < tree_node_list.length; i++) {
        if(tree_node_list[i].dist > max_node.dist) {
            max_node = tree_node_list[i];
        }
    }
    return max_node.node_id;
}

public int getMaxDist(TreeNode[] tree_node_list) {
    int tmp_node_id = getMaxDistTreeNode(tree_node_list, 1);//找到离1节点最远的点
    int max_dist_node_id = getMaxDistTreeNode(tree_node_list, tmp_node_id); //找到离tmp_node_id最远的点
    return tree_node_list[max_dist_node_id].dist;
}

上面所写的代码就是求解树的直径的代码,接下来我们构造原题中的样例。

利用树的直径求解该问题的代码
//初始化
TreeNode[] tree_node_list = new TreeNode[4];
for(int i = 1; i < 4; i++) {
    tree_node_list[i] = new TreeNode();
    tree_node_list[i].node_id = i;
}

//添加边连接关系
tree_node_list[1].friends.add(tree_node_list[2]);
tree_node_list[2].friends.add(tree_node_list[1]);

tree_node_list[2].friends.add(tree_node_list[3]);
tree_node_list[3].friends.add(tree_node_list[2]);

//求树的直径
int max_dist = getMaxDist(tree_node_list);
System.out.println("max dist:  " + max_dist);

//求解最终答案
int ans_step = 0;
int tmp_dist = max_dist;
while(tmp_dist > 1) {
    if(tmp_dist % 2 == 0) {
        tmp_dist = tmp_dist / 2;
    } else {
        tmp_dist = tmp_dist / 2 + 1;
    }
    ans_step += 1;
};
System.out.println("demo ans:  " + ans_step);
max dist:  2
demo ans:  1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值