《程式設計之美》1.4 買書問題 貪心法則
在書中,作者分析兩種解法
解法一是貪心,最後得到的結論是:貪心不成立
解法二是dp , 也類似於遞迴,最後是成立的
在這裡我們重點分析貪心法不成立的原因,以及如何改進
貪心法的適用有兩個必要條件,即優化子結構和貪心選擇性。優化子結構是成立的,在書中的解法二已經證明了。
對於貪心選擇性:最基本的理解就是,每次選擇當前最優的步驟,到最後就能得到整個問題的最優解法。我個人認為這只是貪心最基本的解釋。
其實很多問題或者是演算法都不是一成不變的,有時候,演算法或思想是這樣的,但我們可以在基本思想不變的情況下,根據題目進行一些改進或者是加入一些符合題目的條件(情況)。
對於買書這個折扣如下:
我們來看幾個例子:
1、 2 2 2 1 1
這個例子我們可以這樣分解: 1 1 1 1 1 + 1 1 1 0 0和 1 1 1 1 0 + 1 1 1 0 1
通過折扣的計算,我們可以得到這個的(1 1 1 1 0 + 1 1 1 0 1) 折扣更大。
2、2 2 1 1 0
這個例子我們可以這樣分解: 1 1 1 1 0 + 1 1 0 0 0和 1 1 1 0 0 + 1 1 0 1 0
通過折扣的計算,我們可以得到這個的(1 1 1 1 0 + 1 1 0 0 0) 折扣更大。
3、2 1 1 0 0
這個例子我們可以這樣分解: 1 1 1 0 0 + 1 0 0 0 0和 1 1 0 0 0 + 1 0 1 0 0
通過折扣的計算,我們可以得到這個的(1 1 1 0 0 + 1 0 0 0 0) 折扣更大。
有上面3個例子我們可以得到,就只有第一個例子不符合我們的貪心思想。
那我們是否能這樣,其他情況我們都用貪心來解決,而第3個例子這種情況,我們就特殊處理
下面是我的程式碼:
#include <iostream> #include <algorithm> #include <vector> using namespace std; int num[5]; int ze[5]; //儲存每種折扣有多少個 double kou[4] = { 0.25, 0.2 , 0.1 , 0.05}; int main() { while(1) { int i , max_sum = 0 , j; for(i = 0; i < 5; i++) { cin>>num[i]; max_sum += num[i]; ze[i] = 0; } double sum = 0; i = 0; for(i = 0; i < 5; i++) //i = 0 表示買5本 i = 1 表示買 4本 { sort(num , num+5);//重小到大的排序 if(num[i] == 0) continue; if(i == 2) //這就是特殊情況 //對於特殊情況,我們是把它和 買5本情況 , 重新組合,變成 買兩個 4本 { int x = min(num[i] , ze[0]); ze[0] -= x; ze[i-1] += 2*x; ze[i] = num[i]-x; for(j = i+1; j < 5; j++) num[j] -= num[i]; num[i] = 0; } else { ze[i] = num[i]; for(j = i+1; j < 5; j++) num[j] -= num[i]; num[i] = 0; } } sum = max_sum*8.0; for(i = 0; i < 4; i++) { sum -= (5-i)*ze[i]*8*kou[i]; } cout<<sum<<endl; } return 0; }
通過和書中第二個解法 , 進行的資料測試對比我們得到 , 這種變形的貪心解法是正確的。
具體的分析為什麼是正確的 : 點選開啟連結