题目描述
某人写了n封信,同时为每一封信写1个信封,共n个信封。如果把所有的信都装错了信封,问共有多少种?(这是组合数学中有名的错位问题。著名数学家伯努利(Bernoulli)曾最先考虑此题。后来,欧拉对此题产生了兴趣,称此题是“组合理论的一个妙题”,独立地解出了此题)
试编程求出完全装错情形的所有方式及其总量s。例如,输入n=3,即有3封信需要装入信封,完全装错的一种方式可以表示为312,表示第1封信装入第3个信封,第2封信装入第1个信封,第3封信装入第2个信封。对于n=3,完全装错的方式共有2种,分别是312和231.
输入
输入一个正整数n(2<=n<=6)
输出
输出完全装错情形的所有方式以及装错方式的总量s (每行输出5种方式,一行中的相邻两种方式之间用1个空格隔开。装错方式输出时,从小到大排列,见输出样例)。
样例输入
4
样例输出
2143 2341 2413 3142 3412
3421 4123 4312 4321
s=9
3421 4123 4312 4321
s=9
思路
1,每个信封从信1开始放,直到放到信6,输出符合的摆法
2,如果当前信封不符合则返回上一个信封,改变信封里的信
#include <stdio.h>
int main()
{
int n, i, j, flat, a[30];
int s = 0;
scanf("%d", &n);
i = 1; //第i封信
a[i] = 2; //第i封信在第几个信封
while (1)
{
flat = 1; //判断,满足条件为1,不满足为0
if (a[i] != i) //第i封信不在第i个信封
{
for (j = 1; j < i; j++) //循环,与前面放好的信做比对
{
if (a[j] == a[i]) //第i封信和第j封信在同一个信封,不满足条件
{
flat = 0;
break;
}
}
}
else flat = 0; //第i封信在第i个信封,不满足条件
if (flat && i == n) //已经到最后一封信了,前面的信也都可以放到满足条件的位上了
{
for (j = 1; j <= n; j++) //所有条件均满足,输出
{
printf("%d", a[j]);
}
if (++s % 5 != 0)
{
printf(" ");
} //输出一个解后,加空格
else //s计算解的个数,每输出5个解,换行
{
printf("\n");
}
}
if (flat && i < n) //前面的信放到满足条件的位上了,且还没到最后一封信
{
i++; //下一封信封
a[i] = 1; //信封从信1开始试
continue;
}
while (a[i]== n && i > 0) //第i封信已经试到最后一个信封了,且i>0
i--; //调整或回溯,调整前面的信
if (i > 0) a[i]++; //把第i封信位置前移
else break; //前面没有信可以调位置了,break
}
printf("\ns=%d", s);
return 0;
}
仅看代码可能还是难理解,这里以输入3举例
i=1
a[1]=2
第一次循环 第十次循环
i=2 i=3
a[2]=1 a[3]=2
第二次循环 第十次循环
i=3 i=1
a[3]=1 a[1]=3
第三次循环 第十一次循环
i=3 i=2
a[3]=2 a[2]=1
第四次循环 第十二次循环
i=3 i=3
a[3]=3 a[3]=1
第五次循环 第十三次循环
i=2 i=3
a[3]=4 a[3]=2
第六次循环 第十四次循环
i=2 a[1]=3,a[2]=1,a[3]=2输出312
a[2]=2
第七次循环
i=2
a[2]=3
第八次循环
i=3
a[3]=1
第九次循环
a[1]=2,a[2]=3,a[3]=1 输出231