1.错排问题很早就别研究起来,并且形式多样。最早的错排是伯努利-欧拉信封问题,n封信装入n个信封结果没有一封装入正确的地址的方案数。类似的还有写贺卡问题,n个人互相写贺卡互相赠送自己写的不能赠送给自己,问赠送的方案数。还有拿出来n本书放回去的时候恰好都不在原来的位置的方案数。等等很多类似的问题,错排的准确的意义就是每个元素都不在自己原来的位置。如果只是有几个元素不在自己原来的位置,不能叫做错排,这叫做非完全错排,当然这也是很好解决的,只要在这几个人中间进行错排,又转化为错排的问题。
2.错排的公式:错排的公式有很多种,每一种意义下的都有不同的公式,但是殊途同归,结果都是一样的,只不过出发点不同。我们给出几种错排的公式,但是只证明其中一种,其他的有兴趣的可以自己证明。
普通意义下的错排:D(n)=(n-1)*(D(n-1+D(n-2)),其实这个公式的推导很简单的,我们来严格证明一下(口嗨)。我们考虑第一个元素a1,他的位置可以有n-1中选择,假设它现在是在第i个位置,那么对于第i个元素就会有两种选择。
(1)i在a1原来的位置。那么其实a1和ai独立于整个排列。也就是D(n-2)
(2)i不在原来的位置,那么也很简单,这个时候a1已经排列好了,那么就可以丢掉了,继续考虑ai占据的这个元素的位置,其实也就是D(n-1)转移过来。
综合1,2可以得到。D(n)=(n-1)(D(n-1)+D(n-2)).当然通过容斥也是可以推导出来的,以及给出O(1)的公式。
容斥原理推导的错排公式:D(n) = n! - n!/1! + n!/2! - n!/3! + … + (-1)^n*n!/n! = ∑(k=2~n) (-1)^k * n! / k!,
简化版的公式:D(n) = [n!/e+0.5] ,其中e是自然对数的底,[x]为x的整数部分。
3.小数据范围下的错排数:
D(0) = 1(所有的元素都放回原位、没有摆错的情况)
D(1) = 0(只剩下一个元素,无论如何也不可能摆错)
D(2) = 1(两者互换位置)
D(3) = 2(ABC变成BCA或CAB)
D(4) = 9
D(5) = 44
D(6) = 265
D(7) = 1854
D(8) = 14833
D(9) = 133496
4.几个裸题:
//HDU1465
using namespace std;
ll mis[30];
int n;
void init()
{
mis[0] = 1;
mis[1] = 0;//一个元素不会排错
for (int i = 2; i <= 30; i++)
{
mis[i] = (i - 1)*(mis[i-1] + mis[i - 2]);
}
}
int main()
{
int n;
init();
while (~scanf("%d", &n))
{
printf("%lld\n", mis[n]);
}
return 0;
}
l范围内只在不取模的情况下只能将mis计算到23,也就是D(22)是正确的。大于这个范围就是爆掉ll
//HDU2068
using namespace std;
ll C(int n, int m) //组合数公式
{
ll u, d, i; //组合数公式中的 分子u和分母d
if (m>n / 2) m = n - m; //防止溢出
for (u = d = i = 1; i <= m; i++)
{
u = u * (n - i + 1);
d = d * i;
}
return u / d;
}
int main()
{
int i, M, N;
ll f[14] = { 1,0,1 }, sum;
for (i = 3; i <= 13; i++)
f[i] = (i - 1)*(f[i - 1] + f[i - 2]);
while (scanf("%d", &N), N)
{
for (sum = i = 0; i <= N / 2; i++)
sum += C(N, N - i)*f[i];
printf("%lld\n", sum);
}
return 0;
}
//HDU2049
using namespace std;
ll C(int n, int m)
{
ll u, d, i;
if (m>n / 2) m = n - m;
for (u = d = i = 1; i <= m; i++)
{
u = u * (n - i + 1);
d = d * i;
}
return u / d;
}
int main()
{
int i;
int m, N;
ll f[20] = { 1,0,1 }, sum;
for (i = 3; i <= 20; i++)
f[i] = (i - 1)*(f[i - 1] + f[i - 2]);
int t;
cin >> t;
while (t--)
{
while (cin >> N >> m)
{
/* for (sum = i = 0; i <= m; i++)
sum += C(N, N - i)*f[i];*/
sum = C(N, m)*f[m];
printf("%lld\n", sum);
}
}
return 0;
}