PAT 發郵件 (錯排問題)
阿新 • • 發佈:2018-12-22
這是一個非常經典的數學問題, 錯排問題
組合學中有這樣一個問題:某人給五個朋友寫信,邀請他們來家中聚會。請柬和信封交由助手去處理。粗心的助手卻把請柬全裝錯了信封。請問:助手會有多少種裝錯的可能呢?
這個問題是是由當時有名的數學家約翰·伯努利(Johann Bernoulli,1667—1748)的兒子丹尼爾·伯努利(Danid Bernoulli,17OO一1782)提出來的。瑞士著名數學家尤拉按一般情況給出了一個遞推公式:
用A、B、C……表示寫著n位友人名字的信封,a、b、c……表示n份相應的寫好的信紙。把錯裝的總數為記作D(n)。假設把a錯裝進B裡了,包含著這個錯誤的一切錯裝法分兩類:
(1)b裝入A裡,這時每種錯裝的其餘部分都與A、B、a、b無關,應有D(n-2)種錯裝法。
(2)b裝入A、B之外的一個信封,這時的裝信工作實際是把(除a之外的)n-1份信紙b、c……裝入(除B以外的)n-1個信封A、C……,顯然這時裝錯的方法有D(n-1)種。
總之在a裝入B的錯誤之下,共有錯裝法D(n-2)+D(n-1)種。
a裝入C,裝入D……的n-2種錯誤之下,同樣都有D(n-1)+D(n-2)種錯裝法,因此D(n)=(n-1)[D(n-1)+D(n-2)]
這是遞推公式,令n=1、2、3、4、5逐個推算就能解答蒙摩的問題。
D(1)=0,D(2)=1,D(3)=2,D(4)=9,D(5)=44
答案是44種。
記著公式會用就好了, 有興趣的同學可以推演一下, 不算太難理解
//發郵件 錯排問題 #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <vector> #include <queue> #include <cmath> using namespace std; #define ms(x, n) memset(x,n,sizeof(x)); typedef long long LL; const LL maxn = 25; LL cont[maxn]; //cont[i]表示把i個郵件全部裝錯的方案數 void init() { cont[1] = 0, cont[2] = 1, cont[3] = 2; for(int i = 4; i <= maxn; i++) cont[i] = (i-1)*(cont[i-1]+cont[i-2]); } int main() { int n; init(); while(cin >> n){ cout << cont[n] << endl; } return 0; }