zgao的博客

慢慢的积累

全排列DFS思路详解

首先考虑一道奥数题目:

问题一:

□□□ + □□□ = □□□,要将数字1~9分别填入9个□中,使得等式成立。例如173+286 = 459。请输出所有合理的组合的个数。

我们或许可以枚举每一位上所有的数,然后判断每一位上的数需要互不相等且满足等式即可,但是用代码写出来需要声明9个变量且判断。

那么我们把这个问题考虑为一个求这个9个数的全排列问题,即可得到更优雅的解答方式。

问题二:

输入一个数,输出1~n的全排列。

实例:现在我们考虑有1、2、3的3张扑克牌和编号为1、2、3的3个盒子,需要将这3张扑克牌放到3个盒子里,求其所有可能性。

解析:

  1. 首先我们考虑1号盒子,我们约定每到一个盒子面前都按数字递增的顺序摆放扑克牌。于是把1号扑克牌放到1号盒子中。

      2.接着考虑2号盒子,现在我们手里剩下2号和3号扑克牌,于是我们可以把2号扑克牌放入2号盒子中。于是在3号盒子只剩一种可能性,我们继           续把3号扑克放入3号盒子。此时产生了一种排列——{1,2,3}。

      3.接着我们收回3号盒子中的3号扑克牌,尝试一种新的可能,此时发现别无他选。于是选择回到2号盒子收回2号扑克。

      4.在2号盒子中我们放入3号扑克,于是自然而然的在3号盒子中只能放入2号扑克。此时产生另一种排列——{1,3,2};

      5.重复以上步骤就能得到数字{123}的全排列。

1、现在我们用C语言代码描述往每个小盒子中放入所有可能扑克牌的步骤:

for(int i = 1; i <= n; i++){ a[step] = i; //将i号扑克牌放入第step个盒子中 }

2、a是一个装入了所有小盒子的数组,变量step表示当前正处于第step号小盒子。i则表示扑克牌的序号。现在我们需要考虑另外一个问题,则如果一张扑克牌已经被放入别的盒子中,则不能再被放入当前盒子。

因此需要一个book数组标记哪些牌已经被使用。此时我们完善上述代码。

for(int i = 1; i <= n; i++){ if(book[i] == 0){ a[step] = i; //将i号扑克牌放入第step个盒子中 book[i] = 1; // 置1表示第i号扑克牌不在手中 } }

现在对于step号盒子已经处理完,那么我们要考虑step+1号盒子。第step+1个的盒子的处理方式与第step个盒子的处理方式完全一样。因此,我们可以对上述操作做一个封装。

void dfs(int step)

//step表示当前要处理的盒子

 for(int i = 1; i <= n; i++)

if(book[i] == 0)

{ a[step] = i; //将i号扑克牌放入第step个盒子中

 book[i] = 1; // 置1表示第i号扑克牌不在手中 

} } }

于是我们重新回想文章开头阐述的放置扑克牌的思路:

我们在当前盒子放置完第i个扑克牌之后,便立即处理下一个盒子。于是:

void dfs(int step)

//step表示当前要处理的盒子 

for(int i = 1; i <= n; i++)

if(book[i] == 0)

a[step] = i; //将i号扑克牌放入第step个盒子中 

book[i] = 1; // 置1表示第i号扑克牌不在手中

 dfs(step+1); //递归调用

 book[i] = 0; // 非常重要,收回该盒子中的扑克牌才能进行下一次尝试。

 } } }

需要注意到的是,我们需要收回每一次尝试的扑克牌i,才能进行下一次尝试。

现在需要考虑最后一个问题,那就是什么时候得到一个满足要求的排列,也就是考虑终止条件。这里很容易得到,当我们处理完成第n个盒子的时候,就已经得到一个符合要求的排列了。加上终止条件的代码如下:

void dfs(int step){

 //step表示当前要处理的盒子 

if(step == n+1)

//输出排列 

for(i = 1; i <= n; i++) printf("%d", a[i]);

 printf("\n"); return; } 

for(int i = 1; i <= n; i++)

if(book[i] == 0)

{ a[step] = i; 

//将i号扑克牌放入第step个盒子中 

book[i] = 1; 

// 置1表示第i号扑克牌不在手中 

dfs(step+1); //递归调用

 book[i] = 0; // 非常重要,收回该盒子中的扑克牌才能进行下一次尝试。 } } }

现在深度优先搜索(DFS)的基本模型展现在我们眼前。

其核心在于,在当前步骤要把每一种可能性都尝试一遍(使用for循环),解决完当前步骤后进入下一步。而下一步的解决方式完全等同于当前步骤的解决方法。于是可以总结出DFS的基本模型:

void dfs(int step){ 

*判断结束边界* 尝试每一种可能 

for(i = 1; i <= n; i++)

尝试下一步 dfs(step + 1);

 } return; }



阅读更多
文章标签: dfs
个人分类: 算法
下一篇大数相减
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭