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

博客围绕错排问题展开,以HDU抽奖活动无人中奖为例引出问题,即计算n个人抽奖都不中奖的概率。介绍了错排问题的定义,即n个数字排列使所有数字不在对应序号位置上。并通过分析得出错排方案数的递推公式dp[n]=(n - 1)⋅(dp[n - 1]+dp[n - 2])。

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个数字的排列,使所有的数字都不在自己所对应序号的位置上,这样的一个排列就称为原排列的一个错排,求所有可能的错排的个数。

分析

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

#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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值