java實現 n人過橋問題


java實現 n人過橋問題

【問題描述】

n個人要晚上過橋,在任何時候最多兩個人一組過橋,每組要有一只手電筒。在這n個人中只有一個手電筒能用,求這些人過橋所用的最短時間。

【輸入】

輸入的第一行給出n,接下來的n行給出每個人的過橋時間
例如: 5 1 2 3 4 5

【輸出】

輸出的第一行給出所有n個人過橋的總的秒數,接下來的若干行給出實現策略。每行包括一個或兩個整數,表示組成一組過橋的一個或兩個人,以所用的時間標識。
5
1 2 3 4 5
去: 1, 2
回: 1
去: 4, 5
回: 2
去: 1, 3
回: 1
去: 1, 2
最短時間: 16

【問題分析】

當看到求最短、最長、最多、最少等字樣的時候,大概率會是動態規划或者貪心算法的問題。n人過橋問題是比較典型的貪心算法,由於一次過橋最多兩人且手電筒需要往返傳遞,因此以兩個成員過橋為一個分析單位,計算過橋時間。首先按過橋時間順序從小到大排序,nums[1]為最小,nums[n]為最大。

我們可以這么思考貪心算法的問題:將貪心問題分為多個子問題,這些子問題相互之間沒有關聯(動態規划的每一個子問題都會相互關聯,這是與貪心算法的主要區別),且每個子問題的狀態都是獨立的。那么我們可以這樣思考n人過橋問題:每個子問題所求的都是過去兩人所花費的最短時間,且手電要回到起始點。貪心就貪在每次我都想讓兩個最費時的人過去。

基於上述思想,問題簡化為如何求得最費時的兩個人過去所花費的最短時間呢?這里有點拗口哈。有兩種方案:

方案一:用最快的成員nums[1]傳遞手電筒幫助最慢的nums[n]和nums[n-1]過橋,易知來回所用的時間為2*nums[1]+nums[n]+nums[n-1]。

方案二:用最快的成員nums[1]和次快的成員nums[2]傳遞手電筒幫助最慢的nums[n]和nums[n-1]過橋,具體方案如下:

第一步:nums[1]和nums[2]到對岸,所用時間為nums[2];

第二步:nums[1]返回,將手電筒給最慢的nums[n]和nums[n-1],並且nums[n]和nums[n-1]到對岸后將手電筒交給nums[2],所用時間為:nums[1]+nums[n];

第三步:nums[2]返回,所用時間為nums[2];

綜合起來方案二所用的總時間為2*nums[2]+nums[n]+nums[1]。

最后,不論是動態規划還是貪心算法,都需要考慮邊界條件

顯然,這里的邊界條件是: 最后尚未過橋的人可能有3人或者2人,如果是3人,則最少過橋時間為nums[1]+nums[2]+nums[3],如果是2人,則是nums[1]+nums[2]。下面是算法實現:

public class Test {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 讀取人數
        int[] nums = new int[n+1];
        Arrays.sort(nums);
        for (int i = 1; i <= n; i++) {
            nums[i] = sc.nextInt();
        }
        int result = 0;
        int stay = n;
        while(stay > 3){
            if(nums[1] + nums[stay-1] > 2*nums[2]) {
                // 第二種方案
                System.out.println("去: " + nums[1] + ", " + nums[2]);
                System.out.println("回: " + nums[1]);
                System.out.println("去: " + nums[stay-1] + ", " + nums[stay]);
                System.out.println("回: " + nums[2]);
                result += 2*nums[2] + nums[stay] + nums[1];
            } else {
                // 第一種方案
                System.out.println("去: " + nums[1] + ", " + nums[stay]);
                System.out.println("回: " + nums[1]);
                System.out.println("去: " + nums[1] + ", " + nums[stay-1]);
                System.out.println("去: " + nums[1]);
                result += 2*nums[1] + nums[stay] + nums[stay-1];
            }
            stay -= 2;
        }

        if (stay == 3){
            System.out.println("去: " + nums[1] + ", " + nums[3]);
            System.out.println("回: " + nums[1]);
            System.out.println("去: " + nums[1] + ", " + nums[2]);
            result += (nums[1] + nums[2] + nums[3]);
        } else {
            System.out.println("去:" + nums[1] + ", " + nums[2]);
            result += (nums[2]);
        }
        System.out.println("最短時間: " + result);
    }
}

當然,如果不需要顯示來回的過程,只需要把打印語句去掉即可,這樣使代碼變得簡便:

public class Test {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 讀取人數
        int[] nums = new int[n+1];
        Arrays.sort(nums);
        for (int i = 1; i <= n; i++) {
            nums[i] = sc.nextInt();
        }
        int result = 0;
        int stay = n;
        while(stay > 3){
            if(nums[1] + nums[stay-1] > 2*nums[2]) {
                // 第二種方案
                result += 2*nums[2] + nums[stay] + nums[1];
            } else {
                // 第一種方案
                result += 2*nums[1] + nums[stay] + nums[stay-1];
            }
            stay -= 2;
        }
        if (stay == 3){
            result += (nums[1] + nums[2] + nums[3]);
        } else {
            result += (nums[2]);
        }
        System.out.println("最短時間: " + result);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM