1. 程式人生 > >迅雷2018筆試——尋找組合

迅雷2018筆試——尋找組合

題目:       

給定整數n,取若干個1到n的整數可求和等於整數m,程式設計求出所有組合的個數。 比如當n=6,m=8時,有四種組合:[2,6], [3,5], [1,2,5], [1,3,4]。限定n和m小於120

        一開始沒有什麼解題思路,考慮在迴圈內一遍一遍算,滿足條件了Count++,實際上發現這個過程太複雜,並且要考慮重複的問題。後來發現了一個演算法叫 01揹包問題 實際上就是解決這個題目的思想所在。

所以下面先講這個問題:

0-1 揹包問題:給定 n 種物品和一個容量為 C 的揹包,物品 i 的重量是 Wi,其價值為 Vi 。

問:應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?

我們可以先把這n種物品放到一個數組中用 i 代替:{ 0 , 1 , 2 , 3 , ....., i }

同樣把揹包容量放在一個數組中用 j 代替:{ 0 , 1 , 2 , 3 , ....., j }

設 m[i][j] 為當有 i 種物品,揹包容量為 j 時,揹包中物品的最大總價值。

當考慮物品 i 是否應該放入揹包容量為 j 的時候,我們可以很輕易地得出兩種情況:

(1) j < Wi 的情況,即揹包容量小於物品重量的情況。在這種情況下,很明顯不滿足條件,因此不能拿該物品。

 那麼此時,揹包中物品的總價值就是:m[ i ][ j ] = m[ i-1 ][ j ]  (即總價值和 i -1 種物品時的情況相同)

(2) j >= Wi 的情況,這時揹包容量可以放下第 i 件物品,我們就要考慮拿這件物品是否能獲取更大的價值。

        如果拿取,在拿取該物品的情況下,揹包剩餘容量就變為 j-Wi ,那麼在這容量下,剩餘 i-1種物品的最大價值就是 m[ i-1 ][ j-Wi ] ,也是相當於為第i件物品騰出了w[i]的空間。現在加上該物品,那就加上它的價值Vi。

得出 m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + Vi   

        如果不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)

究竟是拿還是不拿,自然是比較這兩種情況那種價值最大:max { m[ i-1 ][ j ] , m[ i-1 ][ j-Wi ]  + Vi) }

這裡,我們就推出了該演算法的核心方程,稱之為狀態轉移方程:

狀態轉移方程:m[i,j]=max { m [ i-1 , j ] , m [ i-1 , j-wi ] +vi }

可以舉個例子:

物品編號  i 1 2 3 4
物品重量  Wi 2 3 4 5
物品價值  Vi 3 4 5 6

畫出下面的價值表格,可以很容易初始化(當揹包容量為 0 或者物品數量為 0 時,價值都為0):

i/j 0 1 2 3 4 5 6 7 8
0 0 0 0 0 0 0 0 0 0
1 0
2 0
3 0
4 0

然後開始根據上面解釋過的策略開始填值:

i/j 0 1 2 3 4 5 6 7 8 ... i
0 0 0 0 0 0 0 0 0 0
1 0 0 3 3 3 3 3 3 3
2 0 0 3 4 >3 = 4 4 7 3+4=7 > 3 7 7
3 0 0 3 4 5 7 5+2=8 > 7 5+4=9>7 9
4 0 0 3 4 5 7 8 9 10
... ......
j max { m[ i-1 ][ j ] , m[ i-1 ][ j-Wi ]  + Vi) }

自己跟著思路填一遍,就會有所啟發。可以自己隨意舉例填表。

言歸正傳

現在回頭看迅雷的筆試題,是不是覺得很熟悉了。

同樣可以很容易地畫出表格:

下面給出我的一個程式碼,用遞迴解決:

int Practice::ComboNumber(int m, int n)
{
    if(m <1|| n<1)  //m或n為0,無組合
        return 0;  

    int count = 0;

    if(m < n)
    {
        count += ComboNumber(m-1,n);    //m-1情況的組合數
        count += ComboNumber(m-1,n-m);  //再加上n-1,n=n-m時候組合
    }
    else if(m == n)
    {
        count+=ComboNumber(m-1,n);  //m-1情況的組合數
        count++;                    //再加上本身為一種組合
    }
    else if(m>n)
        count+=ComboNumber(m-1,n);  //跟m-1時組合數相同

    return count;
}

對照著表格,你會發現規律的。