1. 程式人生 > >acm中的“組合數”和“錯排”

acm中的“組合數”和“錯排”

題目描述

快遞小哥每天都辛苦的送快遞,今天他需要送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;
}