道路建設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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章