大家高中数据额应该学过全排列吧?比如给大家1,2,3三个数,全排列共有123,132,213,231,312,321,即A32=6,共6种全排列方式。
如果使用for循环,可以用如下代码实现操作:
int i,j,k;
for(i=1;i<=3;i++)
for(j=1;j<=3;j++)
for(k=1;k<=3;k++)
if(i!=j && i!=k && j!=k)
printf("%d%d%d",i,j,k);
代码看起来简单明了很容易实现,不就是三层for循环嘛?但是如果是1,2,3,4四个数全排列呢?你会说,那就写四层for循环呗,这还不简单?但是如果是n个数进行全排列呢,难道要写n层for循环吗?这样做显然是耗费时间和精力的。
例题引入:熊大,熊二,熊泉清三个人要过生日了,小牛准备了3份生日礼物,分别标记为礼物1,礼物2,礼物3。每只熊只能领到一份生日礼物,但是领到礼物几就不一定了,需要小牛手动分发。小牛想知道,共有几种分发礼物的方法?
这时候,小牛先走到熊大面前,但是犹豫是给礼物1呢,还是给礼物2呢,还是给礼物3呢?似乎给礼物几都可以,既然如此,小牛心想:走到一只熊面前,都按照礼物序号从小到大分发。
根据这个决规定,小牛把礼物1给了熊大,此时手里还有礼物2礼物3,分别分给了熊二和熊泉清。然后此时不需要再考虑有没有其他人需要礼物了,因为礼物只准备了3份。此时有排列为“礼物1 礼物2 礼物3”。
是不是只有这一种分法呢?显然不是,这时候,小牛重新回到熊泉清面前,尝试看看能不能不给熊泉清礼物3,把礼物3拿回来然后给熊泉清其他序号的礼物,结果小牛发现,自己手里只有礼物3了,没有更好办法的情况下,小牛只能去拿回熊二手里的礼物2,现在小牛手里有两份礼物了,分别是礼物2和礼物3,按照之前的约定,这次把礼物3给熊二,把礼物2给熊泉清。此时产生了新的排列“ 礼物1 礼物3 礼物2”。
按照以上的步骤,便会产生6种分发礼物的方法,那么如何用代码实现呢?我如何知道,几号礼物在自己手里,几号礼物已经分发出去了呢?此时,我们只需用一个for循环就可以解决问题。
for(i=1;i<=n;i++){//n是礼物的个数
if(book[i]==0){//book[i]=0时表明礼物i还在手中,没有被分出去
a[step] = i;
book[i] = 1;
}
//这里数组表示需要领取礼物的熊的数量
//a[step]表示当前在第step个熊面前
//a[step] = i 表示第礼物i分给了第几只熊
//同时,我们需要记录礼物几已经被分发出去不在小牛手中了
}
如果用函数把以上代码封装起来,就是:
void digui(int step){
for(i=1;i<=n;i++){
if(book[i] == 0){ //book[i]==0表示第i号礼物仍在自己手上
a[step] = i; //将第i号礼物给第step个熊
book[i] = 1; //book[i]设为1,表示第i号礼物不在手中
}
}
}
写好以后,就很好办了,我们只需要分好第step只熊的礼物后,接着分发第step+1只熊的礼物即可,分发第step+1只熊的礼物的办法就是digui(step+1)
void digui(int step){
for(i=1;i<=n;i++){
if(book[i] == 0){ //book[i]==0表示第i号礼物仍在自己手上
a[step] = i; //将第i号礼物给第step个熊
book[i] = 1; //book[i]设为1,表示第i号礼物不在手中
dfs(step+1); //这里是通过函数的递归调用实现(自己调用自己)
book[i] = 0; //一定要把分好的礼物再收回,才能进行下一步分发
}
}
}
同时,我们需要判断是否已经把礼物全部分发完毕,总不能没有礼物了,还继续分发对叭!,所以我们需要增加判断语句,进行判断,当全分发完毕后,输出分发的结果。
完整代码如下:
#include<stdio.h>
int a[10],book[10],n;
int dg(int step){//step表示现在在给第几个熊分发礼物
int i;
if(step == n+1){
//此时理论上应该给第n+1只熊分发礼物,也证明前n只熊都得到了礼物
for(i=1;i<=n;i++)
printf("%d",a[i]);
printf("\n");
return ;
}
for(i=1;i<=n;i++){
if(book[i]==0){
a[step] = i;
book[i] = 1;
dg(step+1);
book[i] = 0;
}
}
return ;
}
int main(){
scanf("%d",&n);//n为1-9的整数
dg(1);//首先先给1号熊分发礼物
return 0;
}