1. 程式人生 > >HDU 2068 RPG的錯排(錯排公式+組合數,內有錯排公式詳解)

HDU 2068 RPG的錯排(錯排公式+組合數,內有錯排公式詳解)

題目連結

今年暑假杭電ACM集訓隊第一次組成女生隊,其中有一隊叫RPG,但做為集訓隊成員之一的野駱駝竟然不知道RPG三個人具體是誰誰。RPG給他機會讓他猜猜,第一次猜:R是公主,P是草兒,G是月野兔;第二次猜:R是草兒,P是月野兔,G是公主;第三次猜:R是草兒,P是公主,G是月野兔;......可憐的野駱駝第六次終於把RPG分清楚了。由於RPG的帶動,做ACM的女生越來越多,我們的野駱駝想都知道她們,可現在有N多人,他要猜的次數可就多了,為了不為難野駱駝,女生們只要求他答對一半或以上就算過關,請問有多少組答案能使他順利過關。

Input

輸入的資料裡有多個case,每個case包括一個n,代表有幾個女生,(n<=25), n = 0輸入結束。

Output

1
1

Sample Input

1
2
0

Sample Output

1
1

PS:中文題,題意不用說了,本題需要用到錯排公式和組合數。

錯排公式

所謂錯排就是一列資料,每個數都有自己的編號,錯排簡言之就是排序後每個數都不在自己位置上(應該很好懂吧)。

我們現在以s本書為例,書的編號(1-s),設D(s)為s本書時的錯排序列數,D(s-1)為s-1本書的錯排排序數。

首先我們從s本書裡面挑出一本書編號為m,現在我們把它放在編號為n的書的位置,這樣一共有(s-1)种放法,挑出來編號為n的書放回有兩種情況:

第一種情況:把編號為n的書放在空出的m的書的位置上,這樣兩本書錯位了,滿足錯排的要求。然後剩下s-2本書的位置不動,對剩下的s-2本書進行錯排,我們的問題又回到開始那樣求D(s-2),所以這種情況的錯排的排序序是(s-1)*D(s-2)。

第二種情況:把編號為n的數不放在空出的編號為m書的位置上,等於現在我們要從除去m,n這兩本書的位置,在剩下s-2個位置中選一個。現在我們可以想一個問題,現在我們編號為m的書已經不用管了,現在還要放沒有放回的這一本,加上還沒有選的s-2本書,總共s-1本書。這是不是相當於除去在前面已經錯位編號為m的書,對剩下的n-1本書進行錯排。所以這種情況錯排的排序數為(n-1)*D(n-1)。

所以錯排的遞推公式為:D(n)=(n-1)*(D(n-1)+D(n-2))。

對於這個題,他是要求大於一半排序正確的情況數,我們可以轉化成求小於一半數的錯排序列和,這樣我們只要求出1-n/2本書的錯排的序列和就就行了,但假如我們是在n個女生中選出任m個女生進行錯排,所以我們還要乘以一個C(n,m)。

細節看程式碼:

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e2+10;
const int mod=10007;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
typedef long long ll;
using namespace std;
ll num[maxn];
void inct()//排錯公式
{
    num[1]=0,num[2]=1;
    for(int i=3;i<15;i++)
        num[i]=(i-1)*(num[i-1]+num[i-2]);
}
ll get_C(int n,int m)//求組合數的值
{
    ll sam=1;
    for(int i=1;i<=m;i++)
    {
        sam*=(n-i+1);
        sam/=i;
    }
    return sam;
}
int main()
{
    inct();
    int n;
    while(scanf("%d",&n)&&n)
    {
        ll sum=1;
        for(int i=1;i<=n/2;i++)
            sum+=num[i]*get_C(n,i);//i個女生序號錯的,還要乘上選出i個女生的組合數。
        printf("%lld\n",sum);
    }
    return 0;
}