一、 错排原理的分析:
第一步:将编号为1的元素放在第2到第n个位置之上:有n-1种方法
第二步:错排余下的n-1个元素
1、若1号元素划等号在第k个位置,第二步就是把K这个元素排好(因为K元素相对应的位置上已经被1给占领了)。如果恰好K这个元素也排在了1的位置上,那么接下来的n-2个元素在与它们的编号相等的位置上再进行错排:有f(n-2)种方法
2、若K这个元素恰巧没排在第1个位置上。这时可将第1个位置“看成”第K个位置,于是就还余下n-1个元素的错排,有f(n-1)种方法。
二、错排情况的公式:
根据乘法原理可以得到有N个元素时的错排情况公式有:(n-1)*[(f-2)+(f-1)] [n>2]
三、错排原理的模拟:信封
1封信封 不存在着错排的情况:所有情况为0
2封信封 只有如下一种情况:
3封信封 两种情况
4封信封
对1 号信封做特殊处理,1号信封不放在1号位置的可能有3种情况。即(4-1)
然后再分情况讨论:
1、若1号信封刚好放在3的位置上,而3号信封又刚好放在1的位置上,那么就只能有2、4的错排也就是2只能放在4 4只能放在2上也就是f(n-2)也就是两个元素的错排情况:f(4-2)=1[由上面的分析得出]情况:
2、若1号信封放在3的位置上,而3号信封不知道放在哪,也就是还剩下3封信封的错排情况:f(3)的情况也就是3封信封的错排情况
于是得到:4封信封的错排方法有:3*(1+2)=9种
根据乘法原理:(n-1)*[(f-2)+(f-1)] [n>2]
应用一:
HDOJ2048神、上帝以及老天爷
题目链接地址:http://acm.hdu.edu.cn/showproblem.php?pid=2048
问题分析:
这就是一道典型的错排原理的应用:
N张票的所有排列可能自然是Ann = N!种排列方式
N张票N个人的错排情况:
f(n) = (i - 1) * [f(n - 1) + f(n - 2)]
/*
N张票的所有排列可能自然是Ann = N!种排列方式
N张票的所有错排情况是:(N-1)*(f[N-1]+f[N-2])
*/
#include <iostream>
using namespace std;
int main(){
__int64 error[21],ticket[21];
int i,nCase,n;
char c='%';
error[1]=0; //1个人的错排情况
error[2]=1; //2个人的错排情况
error[3]=2; //3个人的错排情况
ticket[1]=1; //1张票的排列方式
ticket[2]=2; //2张票的排列方式
ticket[3]=6; //3张票的排列方式
for(i=4;i<21;i++){
ticket[i]=i*ticket[i-1];
error[i]=(i-1)*(error[i-1]+error[i-2]);
}
cin>>nCase;
while(nCase--){
cin>>n;
printf("%.2f%c\n",(double)error[n]*100/ticket[n],c);
}
return 0;
}
应用二:
HDOJ2049不容易系列之(4)——考新郎
题目链接地址:http://acm.hdu.edu.cn/showproblem.php?pid=2049
问题分析:
此题目就是先求从N个新郎中找出M个冤大头。方法就不用多讲了,就是求组合Cmn
然后就再利用错排公式
#include <iostream>
using namespace std;
//计算 n!/r!*(n-r)!
__int64 C(int n,int r){
int i;
__int64 nsum;
__int64 rsum;
__int64 n_rSum;
nsum=1;
rsum=1;
n_rSum=1;
if(n==0){
nsum=1;
}
if(r==0){
rsum=1;
}
if(n-r==0){
n_rSum=1;
}
for(i=1;i<=n;i++){
//cout<<"i="<<i<<endl;
//printf("sum:%I64d\n",sum);
nsum=nsum*i;
}
for(i=1;i<=r;i++){
rsum=rsum*i;
}
for(i=1;i<=n-r;i++){
n_rSum=n_rSum*i;
}
return nsum/rsum*n_rSum;
}
int main(){
int nCase,n,m,i;
__int64 error[21];
error[1]=0;
error[2]=1;
error[3]=2;
for(i=4;i<21;i++){
error[i]=(i-1)*(error[i-1]+error[i-2]);
}
cin>>nCase;
while(nCase--){
cin>>n>>m;
printf("%I64d\n",C(n,m)*error[m]);
}
return 0;
}
原文地址: http://blog.csdn.net/jiahui524/article/details/6624977