一、常見遞歸
簡單題:母牛的故事、骨牌鋪方格、一隻小蜜蜂...。
中等題:不容易系列之(3)—— LELE的RPG難題、阿牛的EOF牛肉串。
較難題:神、上帝以及老天爺、不容易系列之(4)——考新郎。
變态題:折線分割平面。
簡單題,顯而易見的規律;中等題,略加思考推出來的規律;較難題,深度思考(其實是我高中數學弱,不會錯排???);變态題,我想不出來,看題解也沒看懂的題。
二、遞推公式:斐波那契型
\[ F(n) = F(n-1) + F(n-2) \]
上述題目除了折線分割平面之外,都是這個公式的變形。
較難題需要用到錯排,今天細講錯排。
三、錯排公式的推導和應用
錯排的定義:一段序列中一共有n個元素,那麼可知這些元素一共有n!種排列方法。假如在進行排列時,原來所有的元素都不在原來的位置,那麼稱這個排列為錯排。而錯排數所指的就是在一段有n個元素的序列中,有多少種排列方式是錯排。
\[ 遞歸關系:D(n)=(n-1)(D(n-1)+D(n-2)) 特别地有D(1)=0,D(2)=1; \\錯排公式:D(n)=(n!)[(-1)^0/0!+(-1)^1/(1!)+(-1)^2/(2!) +(-1)^3/(3!)+......+(-1)^n/(n!)]; \\其中n!=n*(n-1)*(n-2)*......3*2*1 特别地有0!=1\quad 1!=1 \]
遞推思想:
一共分為兩步
第一步:
錯排(不能選擇自己本來就在的位置)第一個元素,在n個位置中任選一個位置,有 n-1 種選法。
第二步:
錯排其餘n-1個元素,也是需要分情況和種類的。因為這需要看第一步的結果,如果第一個元素落在第k個位置上,第二步就需要把k号元素進行錯排,k号元素錯排位置的不同将導緻不同的情況會發生:
①.假設k号元素正好落在了第一個元素的位置,那麼就可以将第一個元素和第k個元素完全剔除出去,因為相當于隻是他們兩者互換了位置,其他元素暫時還沒有發生變動。留下來的n-2元素進行錯排的話,那麼我們就可以得到了D(n-2)種 的錯排方式。
②.若k号元素不排到第一個元素的位置,我們可以暫時将現在排在k号位置的第一個元素剔除出去,生下來的就隻包含k号元素和原來n-2個的元素了。這時,我們可以将原來的第一個元素的位置看做是現在k号元素的原本位置,因為k号元素不能夠放在原來的位置上,是以就相當于是原來的n-2個元素和k共計n-1個元素進行完全的錯排。那麼一共就有D(n-1)種方法。
綜上所述:第二步得到D(n-1)+D(n-2)種方法,第一步是n-1種,由于是分步進行,是以結果為(n-1)*[D(n-1)+D(n-2)]種方法。
四、實戰
①神、上帝以及老天爺
典型的錯排題目神、上帝以及老天爺,這道題的題意就是求所有的組合情況中錯排所占的比例,題目要求的就是這個。
\[ \frac{(n-1)*[D(n-1)+D(n-2)]}{N!} \]
下面就是AC代碼了:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
int s;
double a[20+5] = {0,0,1}; //錯排的數組
double b[20+5] = {2,2,2}; //全排的數組
int i = 3;
cin >> t;
while(t--)
{
cin >> s;
for( ; i <= s; i++)
{
a[i] = (i-1)*(a[i-1]+a[i-2]); //錯排
b[i] = b[i-1]*i; //全排
}
cout << fixed << setprecision(2) << a[s]/b[s]*100 << "%" << endl;
}
}
②不容易系列之(4)——考新郎
這道題的題意為在n對新人裡面挑出m對新人來錯排,那麼實際讓我們求得就是挑出m對新人的方法乘以m對新人的錯排方法。就是下面這個公式。
\[ C_n ^m*(n-1)*[D(n-1)+D(n-2)] \]
下面是AC代碼:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll factorial(int n, int m) //求組合數
{
ll ans = 1;
if(m < n-m)
m = n-m;
for(int i = m+1; i <= n; i++)
ans *= i;
for(int j = 1; j <= n - m; j++)
ans /= j;
return ans;
}
int main()
{
int t;
int n, m;
ll a[20+5] = {0,0,1};
int i = 3;
int temp;
cin >> t;
while(t--)
{
cin >> n >> m;
if( n < m)
{
temp = n;
n = m;
m = temp;
}
for( ; i <= m; i++) //錯排
a[i] = (i-1)*(a[i-1]+a[i-2]);
cout << factorial(n,m)*a[m] << endl;//輸出錯排*組合數
}
}
求組合數代碼(用階乘不到20就超出long long的範圍了):
typedef long long ll;
ll factorial(int n, int m) //求組合數
{
ll ans = 1;
if(m < n-m)
m = n-m;
for(int i = m+1; i <= n; i++)
ans *= i;
for(int j = 1; j <= n - m; j++)
ans /= j;
return ans;
}
轉載于:https://www.cnblogs.com/trirabbits/p/10108373.html