組合數計算

2022-06-05 13:00:09 字數 1735 閱讀 4625

計算組合數的題目及其變體還是比較常見的,這裡記錄一下幾種經典解法。

我們以乙個典型問題為例進行討論: leetcode 62. unique paths,問的是 mxn 的矩陣從左上走到右下有幾種走法,每次只能向下走或者向右走。

第一種解法是dp,採用遞推公式 \(c(m, n)=c(m-1, n) + c(m-1, n-1)\)(即,楊輝三角)來計算。

很顯然,到達每乙個格仔的走法,是他的左邊和上邊鄰居的走法之和。這樣我們可以使用乙個二維陣列來對應記錄每乙個位置的走法數,空間開銷o(m*n)。進一步,由於我們是一行一行進行計算,我們可以把空間壓縮到o(min(m,n))

/*

* * [62] unique paths

*/// @lc code=start

class solution

}return dp[n];

} // ac, dp

};// @lc code=end

我們注意到,由於題目限制,實際上路徑總長是 m-1+n-1,需要從中選出 m-1 步向下。

第二種解法是直接使用組合數公式 \(c(n, k) = n! / (k! * (n-k)!)\) 進行計算,但是這裡有乙個陷阱是整數溢位問題。

一種解決溢位的辦法是使用浮點數,甚至有人提出了使用對數降低計算複雜度的方案;但是這是乙個整數問題,我們想使用純整數來解決。

注意到計算公式的分子分母各自都是n個數的乘積,所需乘法次數相同。我們把兩遍的計算分為兩段,分別長為kn-k,其中一段是完全相同的1*2*3*...可以直接消除,我們選擇消較長的一段。不妨設剩下的較短一段是長度為k的那一段,則 \(c(n, k) = (n*(n-1)*(n-2)*...*(n-k+1)) / (1*2*3*...*k)\)。這裡的問題仍然是溢位如何解決,但是進展是分子分母都已經是單個自然數序列了。

我們可以在一輪迴圈中解決這個計算,即變形為 \(c(n, k) = n/1 * (n-1)/2 * (n-2)/3 * ... * (n-k+1)/k\)。也就是乘法和除法交叉計算,保證不會出現小數。

據 這篇部落格 所言,有數論中的定理可以保證這一點,但是部落格本身並沒有提供這個定理。

我們這裡簡單說明一下為什麼每次都能整除,不會出現小數。考慮數學歸納,對於第一次操作 n/1 肯定是能整除的;然後我們假設前面 k-1 次都整除了,考察第 k 次能不能整除。當然能整除,因為分母是連續的 k 個自然數,必然有乙個恰好是 k 的倍數,所以其乘積也能被 k 整除。(這裡我們的說明其實不夠充分,因為我們沒有證明,k的時候分母乘積中用到的因子沒有被前面k-1個數用掉。)

/*

* * [62] unique paths

*/// @lc code=start

class solution

return res;

}int uniquepaths(int m, int n) // ac, 組合數c(m+n, m)

};// @lc code=end

其他方案包括 質因數分解;

以及進一步降低了複雜度的 lucas定理;

還有人提出了 二進位制輔助法 的解決思路。

oi wiki | 排列組合

oi wiki | 卡特蘭數

oi wiki | 盧卡斯定理

morning-glory | 卡特蘭(catalan)數入門詳解

計算組合數

1.防溢位 如果直接用c n,m n!n m m 來程式設計很可能會在算n!時就爆了long long,所以每一步最好把除分母也算上。所以對於c n,m 來說取m min m,n m 來算c n,m n n 1 n 2 n m 1 m m 1 m 2 1 顯然分子分母都是m項相乘,從後往前去算 先算...

計算組合數

計算組合數 time limit 1000ms memory limit 32768kb submit statistic problem description 計算組合數。c n,m 表示從n個數中選擇m個的組合數。計算公式如下 若 m 0,c n,m 1 否則,若 n 1,c n,m 1 否則...

計算組合數

time limit 1000 ms memory limit 32768 kib problem description 計算組合數。c n,m 表示從n個數中選擇m個的組合數。計算公式如下 若 m 0,c n,m 1 否則,若 n 1,c n,m 1 否則,若m n,c n,m 1 否則 c n...