問題:
在漆黑的夜里,N位旅行者來到了一座狹窄而且沒有護欄的橋邊。如果不借助手電筒的話,大家是無論如何也不敢過橋去的。不幸的是,N個人一共只帶了一只手電筒,而橋窄得只夠讓兩個人同時過。如果各自單獨過橋的話,N人所需要的時間已知;而如果兩人同時過橋,所需要的時間就是走得比較慢的那個人單獨行動時所需的時間。問題是,如何設計一個方案,讓這N人盡快過橋。
輸入:
第一行是一個整數T(1<=T<=20)表示測試數據的組數
每組測試數據的第一行是一個整數N(1<=N<=1000)表示共有N個人要過河
每組測試數據的第二行是N個整數Si,表示此人過河所需要花時間。(0<Si<=100)
輸出:
輸出所有人都過河需要用的最少時間。
樣例輸入:
1 4 1 2 5 10
樣例輸出:
17
思路分析:
我們可以根據示例的輸出去反推我們選取的策略是不是最優。先假設這四個人是A、B、C、D。那解這道題的一個很自然的想法就是每次讓跑得最快的A陪着另一個過橋,然后A快速地跑回來,再陪下一位過去,最后所有人就都可以過橋了。 讓我們來算一下這要多長時間。為了方便起見,我們把旅行者出發的橋的這一邊稱為“此岸”,而把旅行者想要到達的那邊叫“彼岸”。在表達一個過橋方案時,我們用“←”來表示從彼岸到此岸的移動,用“→”表示從此岸到彼岸的移動。前面“A護送大家過河”的方案就可以寫成:(右邊數字為完成此步驟所需時間)。
A B → 2
A ← 1
A C → 5
A ← 1
A D → 10
所用時間為2 + 1 + 5 + 1 + 10 = 19大於17說明這種情況下這種策略是不行的。但其實有更快的辦法:
A B → 2
A ← 1
C D → 10
B← 2
A B → 2
所用時間為2 + 1 + 10 + 2 + 2 = 17與測試用例相吻合說明這這種策略對於這種情況下是適合的。
這個辦法的聰明之處在於讓兩個走得最慢的人同時過橋,這樣花去的時間只是走得最慢的那個人花的時間,而走得次慢的那位就不用另花時間過橋了。那這樣的策略是不是就一定適合所有的策略呢,我們在這樣的情況下就要多舉出幾個例子來證明自己的觀點。我們可以舉出 1 2 2 9 來看下,可以發現第一種方案其實是比第二種方案更快。說明這個跟測試用例的輸入是有關系的,我們不知道在什么情況下哪種方案最優所以我們需要將我們自己選擇的較優的兩種方案進行比較選擇出花費時間較少的那組。
代碼:
1 import java.util.Arrays; 2 import java.util.Scanner; 3 4 public class 快速渡河 { 5 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 int T = sc.nextInt(); 9 for (int i = 0; i < T; i++) { 10 int n = sc.nextInt(); 11 int[] speed = new int[n]; 12 for (int j = 0; j < n; j++) { 13 speed[j] = sc.nextInt(); 14 } 15 // 排序 16 Arrays.sort(speed); 17 f(n, speed); 18 } 19 } 20 21 private static void f(int n, int[] speed) { 22 int left = n; 23 int ans = 0; 24 while (left > 0) { 25 if (left == 1) {// 只有1人 26 ans += speed[0]; 27 break; 28 } else if (left == 2) {// 只有兩人 29 ans += speed[1]; 30 break; 31 } else if (left == 3) {// 有三人 32 ans += speed[2] + speed[0] + speed[1]; 33 break; 34 } else { 35 // 1,2出發,1返回,最后兩名出發,2返回 36 int s1 = speed[1] + speed[0] + speed[left - 1] + speed[1]; 37 // 1,3出發,1返回,1,4出發,1返回,1,2過河 38 int s2 = speed[left - 1] + speed[left - 2] + 2 * speed[0]; 39 ans += Math.min(s1, s2); 40 left -= 2;// 左側是渡河的起點,left代表左側的剩余人數 41 } 42 } 43 System.out.println(ans); 44 } 45 46 }
結果:
總結一下:
貪心策略簡單來說就是猜出最優的策略,然后再去證明這個策略是否最優,反之換一種策略繼續證明,那證明的過程往往是很困難的,我們可以采用反證的方式,然后多舉例證明策略是否成立。貪心算法的難點就是選取最優策略,還要判斷當前最優是不是整體最優。有時候方案只有一種,有時候方案有很多種,很多種的情況下,我們就需要對其進行比較,因為在某些情況下這種策略是最優的,有些情況下,另外一種策略是最優的,通過比較最終得到的肯定是最優的方案。