wap手机网站制作网站公司网站建设
DFS是一种暴力的搜索,其核心思想是遍历所有方案,一条路走到黑再进行回溯。存储时只需要存储路径。
例1:按字典序输出1-n的全排列。
这就要求我们做到前面已经用过的数字不再出现,因此我们需要一个state数组来去重并且在回溯时恢复现场。
int path[N];//保存序列
int state[N];//数字是否被用过
int n;
void dfs(int u)//u代表当前的层数
{if(u > n)//数字填完了,输出{for(int i = 1; i <= n; i++)//输出方案cout << path[i] << " ";cout << endl;return ;}for(int i = 1; i <= n; i++)//空位上可以选择的数字为:1 ~ n{if(!state[i])//如果数字 i 没有被用过{path[u] = i;//放入空位state[i] = 1;//数字被用,修改状态dfs(u + 1);//填下一个位state[i] = 0;//回溯,取出 i}}
}
例2:P1036 [NOIP 2002 普及组] 选数 - 洛谷
在n个数中选出k个数,并且不同顺序的算一种;相比例1的全排列问题,我们要去掉重复的数量。
因此最后结果要除以n的全排列也就是n的阶乘。
void dfs(int u)
{if (u==k)//当选完k个数后{int sum=0;for (int i=0; i<k; i++){sum+=ans[i];}if (isprimer(sum)){c++;//cout << sum << ' ';}return ;}for (int i=0; i<n; i++){if (!b[i])//判断这个数有没有被选过。{ans[u]=a[i];//选入答案数组中b[i]=true;dfs(u+1);b[i]=false;// 恢复现场}}}
例3:P2404 自然数的拆分问题 - 洛谷
题目可以理解为在1到n这些数中可以重复的选一些数,使其和为n。因此dfs在传参时,可加入一个sum参数。
void dfs(int u,int sum)
{if (sum>n)//当sum大于n时,不符合条件直接结束该路径(我也是在输出结果看到不对后才想起来的){return ;}if (sum==n)//符合预期{int ans[15];if (cun[0]==n){return ;}//剪掉不符合要求的答案for (int i=0; i<u-1; i++){if (cun[i]>cun[i+1]){return ;}//当出现如1 1 1 1 2 1这种重复答案时,直接剪掉,保留1 1 1 1 1 2//cun.clear();}for (int i=0; i<u-1; i++){cout << cun[i] << '+';}//输出最后答案。cout << cun[u-1];cout << endl;return ;}for (int i=1; i<=n; i++){cun[u]=i;dfs(u+1,sum+i);//因为可以重复使用数字,所以无bool数组。//cun[u]=0;}
}
跟例2一样,全排列带来了重复问题
我们只需要判断数组前面的数是否小于后面的数,如果是,就输出。可以有效避免重复问题。