错排问题本质上就是一个动态规划问题,其状态转移方程为:
记d[n]为n个人错排情况的总数。
那么策略可以描述为:分析第n个人错排的可能情况:
1)前n-1个人满足错排的情况,那么第n个人加入后还要错排意味着第n个人与前n-1个人里的任意一个交换字条(共有n-1种交换法)
2)若前n-1个人并不满足错排,但加入第n个人后满足错排。这必然意味着第n个人加入后与前n-1中的某个人交换字条后才满足n个人错排。而又因为原本前n-1人不满足错排,那么前n-1个人中至少有一个人手上的字条是匹配的,那么第n个人就只能与匹配者交换字条。此时可以证明前n-1人中字条匹配者人数不能大于1,可以反证(如果有两人字条匹配,那么在第n个人交换后必然还有一个人字条匹配,不满足)
tip:
printf()输出百分号的时候,有的oj上必须写成%%,有的可以写成%。严格点的话,还是采用第一种写法稳妥。
ac代码如下:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; //错排思想 long long d[30]; long long f(int n){if(d[n]!=-1){return d[n];}else{d[n]=(n-1)*(f(n-1)+f(n-2));return d[n];} } int main(void){memset(d,-1,sizeof(d));d[1]=0;d[2]=1;f(20);int c;scanf("%d",&c);while(c--){int n;scanf("%d",&n);long long cnt1=d[n];long long cnt2=1;for(int i=1;i<=n;i++){cnt2*=i ;}double ans=double(cnt1)/double(cnt2);printf("%.2lf%%\n",ans*100);//printf("%.2lf%\n",ans*100); //这样写就是wronganser }return 0; }