acm中的“組合數”和“錯排”
阿新 • • 發佈:2019-01-28
題目描述
快遞小哥每天都辛苦的送快遞,今天他需要送N份快遞給N個收件人,第i份快遞需要送給第i個收件人。請問其中發生恰好K個送錯了的情況數是多少?
輸入
存在多樣例。每行輸入兩個整數N和K,1≤N≤1000,0≤K≤N。如果兩個都為0,則表示輸入結束,這個樣例不需要處理。
輸出
每行輸出一個樣例的結果,因為數值會比較大,所有結果需要對109+7取模。
樣例輸入
1 1 2 1 3 2 1000 1000 0 0
樣例輸出
0
0
3
37043040
解釋:
組合數和錯排這裡的解法都是用到的遞推
組合數:遞推公式是C(n,m)=C(n-1,m-1)+C(n,m-1);因為對於m個物體中的一個物體,要麼被選,要麼不被選,兩種可能,所以如果被選了,就只需要從剩下的m-1個物體中選n-1個即C(n-1,m-1),
如果沒有被選,就需要從剩下的m-1個物體中選n個即C(n,m-1),然後相加,即C(n,m)=C(n-1,m-1)+C(n,m-1)。
錯排公式:遞推公式為f(n)=(n-1)*(f(n-1)+f(n-2));因為對於n個物體錯排,如果第一個物體錯排的是第k個物體(n-1)種選擇,那麼分析第k個物體,如果第k個物體的位置錯排的是第1個物體那麼剩下的n-2個物體再進行錯排就可以了即f(n-2)
如果第k個物體的位置錯排的不是第1個物體而是w那麼就相當於把第k個物體扔了直接再第一個物體下面排w即f(n-1)然後相加。即f(n)=(n-1)*(f(n-1)+f(n-2))。
程式碼如下
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int mod =1000000007; long long c[1005][1005],f[1005]; void init1() { for(int i=0;i<1005;i++){ c[i][i]=c[0][i]=1; } for(int i=1;i<1005;i++){ for(int j=i+1;j<1005;j++){ c[i][j]=(c[i-1][j-1]+c[i][j-1])%mod; } } } void init2(){ f[0]=1; f[1]=0; f[2]=1; for(int i=3;i<1005;i++){ f[i]=((i-1)*(f[i-2]+f[i-1]))%mod; } } int main(){ int n,k; init1(); init2(); while(~scanf("%d%d",&n,&k),n+k){; printf("%I64d\n",(c[n-k][n]*f[k])%mod); } return 0; }