(1)定义
深度优先搜索也叫深度优先遍历,简称DFS。假设我们需要在某个房间里面寻找钥匙,无论从那一间房都可以,比如主卧。然后从房间的一个角开始,将房间内的墙角、床头柜、床上、床下、衣柜里等所有地方挨个进行寻找,做到不放过任何一个死角,所有的抽屉、存储柜中全部都找遍,形象比喻就是翻个底朝天,然后再寻找下一间,直到找到为止。如果是走迷宫,就是从起点出发,同时进行所有可能路径的寻找,直到某条路径到达终点。与广度优先搜索不同的是,广搜是一条路走到底,深搜就是同时所有可能路径一起搜索,直到有一条路径到达终点,就结束。
(2)图中的定义
从图中某个顶点出发,访问此顶点,然后从V的未被访问的邻接点出发深度优先遍历图,直至图中所有和V有路径相通的顶点都被访问。若图中尚有结点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
(3)例题
八皇后 Checker Challenge
题目描述
一个如下的 6* 6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2 4 6 1 3 5来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。
输入格式
一行一个正整数 n,表示棋盘是 n *n大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
样例 #1
样例输入 #1
6
样例输出 #1
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
提示
【数据范围】
对于 100% 的数据,6<= n <=13。
题目翻译来自NOCOW。
USACO Training Section 1.5
思路
每个皇后会占用他所在的行、列以及两条斜线,所以一行只能放一个皇后,关键是我们要表示出竖线以及斜线的占用方式;
用row[ i ]可以表示第i行是否有了皇后
用t[ i ]=j表示第i行第j列是否已经被占用
确定第 i 行放皇后的位置,搜索每一列,如果(i,j)所在的行、列、上斜线,下斜线都没有被占用,即所有的数据都为0,那就可以放置皇后,更新占用状态,接着搜索下一行放皇后的位置。搜索结束后注意还原。
下图是一个 4*4的坐标表格
1,1
1,2
1,3
1,4
2,1
2,2
2,3
2,4
3,1
3,2
3,3
3,4
4,1
4,2
4,3
4,4
看下标图我们的一得到
上斜线(也就是从(4,1)这个点往点(1,4)的方向,就是“/"):i+j的值是一样的,所以可以用ud[i+j]来表示是否被占用。
下斜线(就是从(4,4)这个点往(1,1)这个点的方向一样,就是“\”):i-j的值都是一样的,加上n是因为下标没有负数,所以加上相同的数,所以用ld[i-j+n]来表示是否被占用。
#include<stdio.h>
int n;
int t[15],row[15],ud[100],ld[100];//用数组t[13]表示皇后放的位置,分别用ud[13]和ld[13]表示上、下对角线
int sum=0,k=0;
int j;
void print()
{
sum++; //统计总共有多少解
if(k<=3) //输出前3个解
{
for(int i=1; i<=n; i++)
printf("%d ",t[i]);
printf("\n");
}
}
void dfs(int i) //i表示第几行
{
if(i>n)
{
k++;
print();
return;
}
else
{
for(int j=1; j<=n; j++)
{
//上斜线的值都是相等的,则可以用ud[i+j]表示,i表示行,j表示列
//下斜线的坐标相减的值是一样的,这个相减的数字加上相应的行数,即ld[i-j+n]
if( row[j]==0 && ud[i+j]==0 && ld[i-j+n]==0 )
{
t[i]=j; //在第i行第j列放了第i个皇后
row[j]=1; //说明这一行已经有了一个皇后
ud[i+j]=1; //上下两条斜线也都有了一个皇后
ld[i-j+n]=1;
dfs(i+1);
row[j]=0;
ud[i+j]=0;
ld[i-j+n]=0;
}
}
}
}
int main()
{
scanf("%d",&n);
dfs(1); //从第一行开始放皇后
printf("%d",sum);
k=0;
return 0;
}