错排详解及应用

一、        错排原理的分析

第一步:将编号为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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值