题目描述如下:
dfs的本质实际上是递归,想要真正理解dfs的代码运行过程,对于刚接触的同学只看代码是很抽象的,这里强烈建议手动模拟一遍代码怎么运行123的全排列,同时不断思考每一步的意义是什么,如果没有这个操作会导致什么,回溯的过程具体是什么样的,达到了什么效果
这里用箱子里边放卡片作为比喻能更清晰的想象出运行过程,其中我们的path数组用来记录n个箱子中分别放了什么内容的卡片,也就是记录排列结果的数据;check数据来记录卡片是否还在你手中;u代表你当前站在哪个箱子面前。当你站在最后一个箱子前时,就找到了一种所有卡片放进箱子里(全排列)的情况,此时我们输出结果
而在第u个箱子面前我们的操作是:用i循环遍历0号卡到最后一张卡牌,依次检查还在不在自己手上,如果不在,用过了(check(i)==true),则不操作这张牌,for循环到下一张牌检查下一张牌是否在手里,若不在,继续找下下一张,直到所有牌都用过(i==n)为止,此时对应循环结束
如果第i张牌还在手里,我们则对其进行如下操作:将牌放到当前u号箱子里,并记录一下(path【u】=i),记录i号牌用掉了(check[i]=true),走到下一个箱子面前(dfs(u+1))
对于最后一行check【i】=false的回溯,我们理解为当u==n,我们走到头的时候,将卡牌i再拿回来,让后再走回上一个箱子面前(由函数递归,向上一层归完成我们向回走的过程),判断能否有其他放法
这里注意由于i是不会回退的,所以不会出现重复情况,即1号牌放入3号箱子后,拿回后i++(1号变2号卡),3号箱子就不会再去让1放进去了,而是将二放进去,这也是很多同学迷的一个点
如果以上说明还是似懂非懂,还是建议手动模拟代码,并反复思考着过程的同时亲手写一下代码
ac代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=10;
int n;
int path[N];
bool check[N];
void dfs(int u){
if(u==n){//当深度u等于数据要求n的时候,即达到树的最深处,盒子的最后一个面前
for(int i=0;i<n;i++){
cout<<path[i]<<" ";
}
cout<<endl;
return;
}
for(int i=0;i<n;i++){
if(check[i]==false){//当前卡片还没被用过
check[i]=true;//放下卡牌到箱子里,该卡用过了
path[u]=i+1;//记录第u个箱子中放入了值为i+1的卡牌
//(由于下标从零开始但是数据是从一开始,卡片内容和卡片索引差一)
dfs(u+1);//走到下一个盒子面前继续如上判断
check[i]=false;//关键回溯的一步,将卡牌从当前箱子中再拿出来
}
}
}
int main(){
cin>>n;
dfs(0);
return 0;
}
题目来源: