題目連結:
題目大意:
給定一正整數 N(N ≦ 800),代表原本有N個物件,編號為 1 ~ N ,排在 1 ~ N 的位置。
求重新排列,使得 N 個物件都不在自己原來的位置上(也就是「錯排」)的方法數為何?
當 N = -1 時,代表輸入結束。
範例輸入:
1
2
3
4
-1
範例輸出:
0
1
2
9
解題思維:
當窮舉方法數的時候,乍看之下可能沒有什麼規律。但是仔細觀察可以發現,一些疑似遞迴的蹤跡。
考慮以下情況,當 N ≧ 3時:
有鑑於第 N 個數字不會排在第 N 個位置,一定會放在1 ~ N-1其中之一。這時,它放的位置假設為 K ,而 K 放的地方有兩種情形。
第一種, K 放在 N 的位置,也就是 N、K 位置互換。那麼,剩下的N-2個數字之原本的位置也就都尚未被占去。因此這個情況等價於 N-2 時的方法數。
第二種, K 沒有放在 N 的位置。由於我們已經把 N 放在 K 的位置,而K絕對不會放在N的位置。我們把放在 K 位置的N拿掉,剩下的物件就像是 N-1 時的情況之排列(K就變成了新的N)。因此等價於 N-1 時的方法數。
又因為N放的時候,可以從1 ~ N-1任意挑一個位置放(也就是K是其中任意數字)。因此K有N-1種可能挑法。
所以, N 的方法數 = (N-1)*(N-1的方法數 + N-2的方法數)。
而又因為N可到800的緣故,因此上述遞迴式的值會增長到突破C++的整數型態上限,為此,我們需要大數運算,詳見
這裡。
此次分享到此為止,如有任何更加簡潔的想法或是有說明不清楚之地方,也煩請各位大大撥冗討論。