1. 程式人生 > >《程式設計之美》1.4 買書問題 貪心法則

《程式設計之美》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;
}

通過和書中第二個解法 , 進行的資料測試對比我們得到 , 這種變形的貪心解法是正確的。

具體的分析為什麼是正確的 : 點選開啟連結