对于代码编写不太有帮助的图解就不放了,模板暂且放在这里,思考几个问题
void dfs(状态x)//参数用来表示状态
{
if(到达终点) {
...//根据题意添加
return;
}
if(不合法)
return;
if(特殊状态)//剪枝
return ;
for(扩展) {
if(扩展所达到状态合法) {
/*修改*/ //根据题意来添加
/*标记*/
dfs(状态x+δ);
/*还原标记*/
//是否还原标记根据题意
//如果加上(还原标记)就是 回溯法
}
}
}
一个问题是否只有一棵树?然后遍历这棵树就行了吗?
一个具体问题的"状态"到底是谁?它又是以怎样的形式作为树的结点?
dfs()的参数代表谁?一个还是多个?
vis[]数组确实有"标记此位置是否访问过"的意义,它的下标又有什么含义?
一个dfs程序被编写之前,我想,这些问题必须被阐明说清,否则仅仅摆出一段代码结果是无意义的。
对于4皇后问题:4x4的网格放置棋子,不允许同行 同列 主对角 副对角 存在其他棋子,要构造这个解空间树
前人给出的想法是:★树的层数 代表格子行数(同时也是下棋步数),节点提供的信息 是这行棋子放置的位置
因此给出以下的形式,★长度为4的一维数组,4个元素自然依次代表了4次放置棋子的列数,数组被填满时即成为了最后的一个解position[current];
![](https://i-blog.csdnimg.cn/blog_migrate/4e389b8c8845de896b7f5015dd188830.jpeg)
由于是逐行摆放1个,因此不用考虑同行互斥,在代码中要判断同列、主副对角线的互斥,所以需要一个记录数组visited[3][1],
★当第一个维度为0,其第二维度的信息 显示同列是否互斥;当第一个维度为1,其第二维度的信息 显示主对角线是否互斥;当第一个维度为2,其第二维度的信息 显示副对角线是否互斥;//暂且接受
★这样一来,遍历时判断互斥仅需检查vis[0|1|2][i|cur+i|cur-i+n]为0还是1,i为列数,行列之和恒定 则在同一主对角,行列之差恒定 则在同一副对角;//暂且接受
#include<bits/stdc++.h>
using namespace std;
int cur,pos[4],vis[3][1];
void dfs(int cur){
if(cur==4){//已经下了4颗,得到一个解,递归出口
/*输出结果*/
}else{
for(int i=0;i<n;i++){//每行自左向右遍历,i为列数
/*若无互斥情况*/
if(!vis[0][i]&&!vis[1][i+cur]&&!vis[2][cur-i+n]){
pos[cur]=i;//将列i填入答案数组
/*互斥记录*/
vis[0][i]=1;
vis[1][i+cur]=1;
vis[2][cur-i+n]=1;
/*开始下一行的求解,递归调用dfs*/
dfs(cur+1);//此时应该已经得到了一个解
/*将互斥记录归零,准备下一个解*/
vis[0][i]=0;
vis[1][i+cur]=0;
vis[2][cur-i+n]=0;
}
}
}
}
在这一问题中仅有一个解空间树,遍历它即可获得答案;它的每个"状态"是每一步(行)棋下的列数,它以数组的形式作为树的结点;dfs()的参数代表步数(行数);vis[]数组的下标含义比较复杂,上文中已经给出。
在此我已经回答了开头提出的全部问题,连循环计数变量的意义也写在了注释里,画★部分需要思考,多希望其他人其他题目的题解,也能对我这种真入门的小白给出明确的答案,而不仅仅是一边喊着我好菜一边只把代码留下。
我这样的普通人平凡人一般人,仅仅接触一道例题 是绝对不足以掌握它去解决新问题的。给出另一道
所谓“绝对没有比这更基础的题了”里面的第1题:全排列问题:给出整数N,求出1到N的全排列。
要构造这个树,仿照上一题,树的层数 代表选数次数,节点提供的信息 是这次选择的数字,依旧假定最终的答案是长度为n的1维数组,只需要依次填入当前选择的数,填满数组即得到答案permutation[current];
由于每次选数不能是之前选过的数,所以需要一个记录数组visited[N]
#include<bits/stdc++.h>
using namespace std;
//全排列问题:给出整数n,求出1到n的全排列。n<10
int N,cur,perm[10],vis[10];
void dfs(int cur){
if(cur==N+1){//已经选过了n个数,得到一个解
/*输出结果*/
}else{
for(int i=1;i<=N;i++){//从1开始尝试选数
/*若i未被选过*/
if(!vis[i]){
perm[cur]=i;//将数i填入答案数组
/*选数记录*/
vis[i]=1;
/*开始下一行的dfs*/
dfs(cur+1);//此时应该已经得到了一个解
/*将记录归零,准备下一个解*/
vis[i]=0;
}
}
}
}
相对于上一题,这题不需要费心设计访问记录,形式几乎一样,是上一题的精简版;