藍橋杯-大臣的旅費

問題描述java

       好久之前,T王國空前繁榮。爲了更好地管理國家,王國修建了大量的快速路,用於鏈接首都和王國內的各大城市。算法

       爲節省經費,T國的大臣們通過思考,制定了一套優秀的修建方案,使得任何一個大城市都能從首都直接或者經過其餘大城市間接到達。同時,若是不重複通過大城市,從首都到達每一個大城市的方案都是惟一的。數組

       J是T國重要大臣,他巡查於各大城市之間,體察民情。因此,從一個城市快馬加鞭地到另外一個城市成了J最常作的事情。他有一個錢袋,用於存放往來城市間的路費。this

聰明的J發現,若是不在某個城市停下來修整,在連續行進過程當中,他所花的路費與他已走過的距離有關,在走第x公里到第x+1公里這一公里中(x是整數),他花費的路費是x+10這麼多。也就是說走1公里花費11,走2公里要花費23。遞歸

       J大臣想知道:他從某一個城市出發,中間不休息,到達另外一個城市,全部可能花費的路費中最可能是多少呢?get

輸入格式

       輸入的第一行包含一個整數n,表示包括首都在內的T王國的城市數class

       城市從1開始依次編號,1號城市爲首都。import

       接下來n-1行,描述T國的高速路(T國的高速路必定是n-1條)原理

       每行三個整數Pi, Qi, Di,表示城市Pi和城市Qi之間有一條高速路,長度爲Di公里。List

輸出格式

       輸出一個整數,表示大臣J最多花費的路費是多少。

樣例輸入1
5
1 2 2
1 3 1
2 4 5
2 5 4
樣例輸出1
135
輸出格式

大臣J從城市4到城市5要花費135的路費。

解法一:

 

解題思路:運用鄰接矩陣存儲兩點間的距離,經過Floyd-Warshall求得任意兩點間的最短路徑,而後在最短路徑中找到最大值

(路費最多首先是路徑最長是吧<*-*>,不先求得最短路徑的話,就能夠沒完沒了的來回走,而後就沒有最長路徑了是吧<*-*>)

JAVA代碼以下:

import java.util.Scanner;

public class Test {
    static int result = 0;

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[][] e = new int[n + 1][n + 1];
        for (int i = 0; i < n + 1; i++) {
            for (int j = 0; j < n + 1; j++) {
                if (i == j)
                    e[i][j] = 0;
                else
                    e[i][j] = 99999999;
            }
        }

        for (int i = 1; i < n; i++) {
            int x = sc.nextInt();
            int y = sc.nextInt();
            int z = sc.nextInt();
            e[x][y] = e[y][x] = z;    //由於是無向圖,因此x到y和y到x的距離是同樣的
        }

        for (int i = 1; i < n + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                for (int k = 1; k < n + 1; k++) {
                    if (e[j][k] > e[j][i] + e[i][k]) {
                        e[j][k] = e[j][i] + e[i][k];
                    }
                }
            }
        }

        int max = e[1][1];
        for (int i = 1; i < n + 1; i++) {
            for (int j = 1; j < n + 1; j++) {
                if (max < e[i][j] && e[i][j] < 99999999) {    //這裏的判斷要注意,咱們須要的是最短路徑中的最大值,99999999表明不能到達
                    max = e[i][j];
                }
            }
        }
        // System.out.println(max); int result = max*10+ max*(1+max)/2;
        System.out.println(result);

    }
}

很遺憾,這個算法複雜度過高,只能拿到75分

解法二:用鄰接表存儲邊經過深度優先搜索的方式將任意兩點間最短路徑的最大值求出來這裏要強調的是

官網上給出的解題思路並不許確由於距離一號頂點的最遠點和距離二號點點的最遠點並不必定是整個路徑

中相距最遠的兩點,而是先找到距離一號頂點的最遠點姑且稱它位a,而後找距離a的最遠點b,這樣a和b

纔是整個路徑中最遠的兩點。

該題目題意就是要求樹的直徑。
求樹直徑原理:以任意點w開始,先作一次DFS(BFS),找到最遠點v,而後以此點v,進行一次DFS(BFS),
找到最遠點u,u到v就是樹的直徑,記作d(u,v)。
證實:
1) 若是w 是直徑上的點,則v顯然是直徑的終點(由於若是v不是的話,
       則一定存在另外一個點x使得w到x的距離更長,則與d(u,v)爲直徑衝突)
2)若是w不是直徑上的點,則w到v必然於樹的直徑相交(反證),那麼交點到v
    必然就是直徑的後半段了,因此v必定是直徑的一個端點,因此從v進行DFS獲得的必定是直徑長度
代碼以下:

import java.util.ArrayList;
import java.util.Scanner;
public class PREV_9{


    static int max = 0;        //最遠距離
    static int fina = 0;
    static boolean first = true;
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        
        int[] book = new int[n + 1];
        @SuppressWarnings("unchecked")
        ArrayList<Edge>[] s = new ArrayList [n+1]; //定義一個集合數組
        
        for(int i = 0 ; i < n+1 ; i++){
            s[i] = new ArrayList<Edge>();
        }
        
        for (int i = 0; i < n - 1; i++) {
            int d = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            //創建鄰接表
            s[d].add(new Edge(b,c));//由於是無向圖,因此兩點互爲起始點的終點
            s[b].add(new Edge(d,c));
        }
        sc.close();
   //將地圖中每一個點最爲終點進行搜索
        for (int j = 2; j <= n; j++) {
            book[1] = 1;
            dfs(s, book,n, 1, 0, j);
            //System.out.println("---");
        }    
        book[1] = 0;
      
        first = false;
        for (int j = 1; j <= n; j++) {
            book[fina] = 1;    
            dfs(s, book,n, fina, 0, j);
        }
     
        System.out.println(max*10+max*(1+max)/2);   //很重要,這是最終結果
 
    }


    private static void dfs(ArrayList<Edge>[] s, int[] book,
            int n,int cur, int dis, int des) {
        // TODO Auto-generated method stub

        if (cur == des) {
     
            if (dis > max) {
                max = dis;
                if(first){   //很重要,這是判斷是否爲第一次找到的最遠點,若是是,將其做爲第二次搜索

                              //的出發點,在第二次搜索中決不容許更新
                    fina = cur;
                }
            }
            return;
        }
        for (int k = 1; k <= n; k++) {
            if(cur==k){     //尋找出發點
               for(int m = 0 ; m < s[k].size() ; m++){
                  if (book[s[k].get(m).to]==0) {
                      int mid = cur;          
                     book[s[k].get(m).to] = 1;   
                     cur = s[k].get(m).to;
                     dfs(s, book,n, cur, dis + s[k].get(m).dis, des);
                     book[cur] = 0;            //很重要,遞歸結束後要取消標記
                     cur = mid;                  //很重要,地櫃結束後要回到遞歸以前的出發點
                  }
               }
            }
        }
        return;
    }

}
//用來存儲邊的信息,該數組的下標表明出發點,to表明終點,dis表明路程
class Edge{
    int to;
    int dis;
    public Edge( int to, int dis) {
        this.to = to;
        this.dis = dis;
    }
}

很遺憾,雖然看似用鄰接表比鄰接矩陣下降了算法複雜度,可是仍然只拿到75分!若有不足之處,請多指點!!!

相關文章
相關標籤/搜索