神、上帝以及老天爷 (错排问题 详细推导)

176 阅读3分钟

👉 “Offer 驾到,掘友接招!我正在参与2022春招打卡活动点击查看 活动详情

hduoj2048 神、上帝以及老天爷

Problem Description

HDU 2006'10 ACM contest的颁奖晚会隆重开始了! 为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的: 首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中; 然后,待所有字条加入完毕,每人从箱中取一个字条; 最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!” 大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!不过,正如所有>>试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖! 我的神、上帝以及老天爷呀,怎么会这样呢? 不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗? 不会算?难道你也想以悲剧结尾?!

Input

输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数。

Output

对于每个测试实例,请输出发生这种情况的百分比,每个实例的输出占一行, 结果保留两位小数(四舍五入),具体格式请参照sample output。

Sample Input

1 2

Sample Output

50.00%

思路:错排问题

错排问题描述

一个n个数字的排列,使所有的数字都不在自己所对应序号的位置上,这样的一个排列就称为原排列的一个错排,求所有可能的错排的个数。

分析

  • dp[n]dp[n]表示n个数字错排的方案数,dp[n1]dp[n-1]表示(n1)(n-1)个数字错排的方案数;
  • 假设对于第MM个位置的数字mm,首先取位置KK的元素放置,此时有(n1)(n-1)种情况;
  • 考虑第KK个位置的数字kk:如果kk放在第MM个位置,此时有(n2)(n-2)个数字进行错排,方案数为dp[n2]dp[n-2];如果kk不在第MM个位置,此时就是(n1)(n-1)个数字进行错排,方案数为dp[n1]dp[n-1];
  • 综上所述,所有的情况就是dp[n]=(n1)(dp[n1]+dp[n2])dp[n]=(n-1)\cdot(dp[n-1]+dp[n-2])

#include<iostream>
#include<cstring>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

typedef long long LL;
typedef pair<int,int> PII;
const int MAXN = 1e6 + 50;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const int INF=0x3f3f3f3f;

#define PI acos(-1)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,x) for(int i=0;i<x;++i)
#define UP(i,x,y) for(int i=x;i<=y;i++) 
#define DOWN(i,x,y) for(int i=x;i>=y;i--)

const int N  = 21;
int C,n; LL dp[N],f[N];

void Init(){
    dp[0] = 0; dp[1] = 0; dp[2] = 1;
    UP(i,3,N) dp[i] = (i - 1) * (dp[i-1] + dp[i-2]);
    f[0] = f[1] = 1;
	UP(i,2,N) f[i] = i * f[i-1];
}
int main(){
	Init();
    scanf("%d",&C);
    while(C--){
        scanf("%d",&n);
        double p =  dp[n] * 1.0 / f[n] * 100;
        printf("%.2f%%\n", p);  //不要用%lf输出double类型 输出%号记得%%
    }
    return 0;
}