6469 Deranged Exams (排列组合 + 容斥原理)

欢迎访问https://blog.csdn.net/lxt_Lucia~~

宇宙第一小仙女\(^o^)/~~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~~

 

----------------------------------------我只是一条可爱哒分界线-------------------------------------------

一、问题:

Description

The first question on the Data Structures and Algorithms final exam has a list of N terms and a second list of N definitions. Students are to match each term with the correct definition.

Unfortunately, Joe, who wrote a Visual BASIC program in high school and assumed he knew all there was to know about Computer Science, did not bother to come to class or read the textbook. He has to guess randomly what the matches are. Let S(N, k) be the number of ways Joe can answer the question and get at least the first k matches wrong.

For this problem, you will write a program to compute S(N, k).

 

Input

The first line of input contains a single integer P, (1 ≤ P ≤ 1000), which is the number of data sets that follow. Each data set should be processed identically and independently.

Each data set consists of a single line of input containing three space separated decimal integers. The first integer is the data set number. The second integer is the number, N (1 ≤ N ≤ 17), of terms to be matched in the question. The third integer is the number, k (0 ≤ k ≤ N), of initial matches to be incorrect.

 

Output

For each data set there is a single line of output. It contains the data set number followed by a single space which is then followed by the value of S(N, k).

 

Sample Input

4

1 4 1

2 7 3

3 10 5

4 17 17

 

Sample Output

1 18

2 3216

3 2170680

4 130850092279664

 

二、题意:

用 n 条释义匹配 n 个术语,求至少前 k 个匹配错误的情况总数 S(n,k)。

 

三、思路:

1)首先,至少前k个匹配错,做起来很麻烦,我们找它的对立事件:至多前k个对。

2)假设n=7, k=3:

     当计算前三个只有一个匹配正确时,我们得到的情况数为 C(3,1)*A(6,6) ,即从前3个中选一个认为对,其他6个乱排。我们发现,这重复计算了有2个匹配正确和有3个匹配正确的情况,于是我们减去有2个匹配正确的情况数C(3,2)*A(5,5),我们又发现这样多减掉了三个匹配正确的情况,于是再加回来C(3,3)*A(4,4)。

最终的到情况总数为 C(3,1)*A(6,6) - C(3,2)*A(5,5) + C(3,3)*A(4,4)。

以此类推,我们就得出了计算方式:取奇数时加,偶数时减,最终求出至多前k个对的情况数。

3)所求 = 所有情况数 - 至多前k个对的情况数 ,即 A(n,n) - sum。

4)下面的代码中,我用 a [ i ] 表示 i 的阶乘,先计算出了a 数组,因为1 ≤ N ≤ 17,用的时候直接取,不必重复计算,省时省力。

a [ k ] / ( a [ k - i ] * a [ i ] ) 就是C(k,i),排列组合公式。emmm... 记得开long long...

 

四、代码:

#include <cstdio>
#define read(x) scanf("%d",&x)
#define fori(a,b) for(int i=a;i<=b;i++)

using namespace std;
typedef long long LL;

LL a[20];

void JieCheng()
{
    a[0]=a[1]=1;
    fori(2,19)
        a[i]=a[i-1]*i;
}

int main()
{
    int T,num,n,k;
    read(T);
    JieCheng();
    while(T--)
    {
        LL sum=0;
        scanf("%d %d %d",&num,&n,&k);
        fori(1,k)
        {
            if(i&1)
                sum+=(a[k]/(a[k-i]*a[i]))*a[n-i];
            else
                sum-=(a[k]/(a[k-i]*a[i]))*a[n-i];
        }
        printf("%d %lld\n",num,a[n]-sum);
    }
    return 0;
}

 

------------------------------------------我也是有底线的----------------------------------------------------

欢迎访问https://blog.csdn.net/lxt_Lucia~~

宇宙第一小仙女\(^o^)/~~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值