深度优先搜索 DFS
深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.
深度优先搜索特别适用于那些探索所有的可能性的这些问题(穷举)。
以例题来熟悉dfs
例题
1.1~n的全排列
#include <stdio.h>
#include <iostream>
using namespace std;
int a[101],v[101],n;//v[]用于标记是否搜索过
void print() //打印排列
{
int i;
for(i=1;i<=n;i++)//去掉了a[0],所以下方a从1开始
{
cout<<a[i]<<' ';
}
cout<<endl;
}
void dfs(int i) //i为a[]的第i位
{
int j;
if(i==n+1) //如果i==n+1,表示前n个数已经排好
{
print();
return;
}
for(j=1;j<=n;j++)//每次搜索从1开始尝试
{
if(v[j]==0)//数字是否使用过
{
a[i]=j;//当前位置存放未使用的数字
v[j]=1;//该位置标记为使用过
dfs(i+1);//进行下一个位置的数字放置
v[j]=0;//回溯,当满足一种全排列后,进行下一种尝试
}
}
}
int main()
{
cout<<"1--n的全排列,请输入n:";
cin>>n;
dfs(1);//dfs(0)是从a[0]开始
return 0;
}
该题模板
int dfs(int i)
{
if(边界)
{
输出解;
}
尝试每一种可能{
if(满足进一步搜索条件)
{
标记;
dfs(i+1);
回溯;
}
}
}
2.凑算式
这个算式中A-I代表1~9的数字,不同的字母代表不同的数字。比如:
6+8/3+952/714 就是一种解法,
5+3/1+972/486 是另一种解法。
这个算式一共有多少种解法?
思路:在1~9的全排列中找符合条件的等式
#include<iostream>
using namespace std;
int ans=0,num[9];//ans=答案
int v[10];//标记是否使用过
void judge(){//判断条件
if((num[0]+(double)num[1]/num[2]+(double)(num[3]*100+num[4]*10+num[5])/(num[6]*100+num[7]*10+num[8]))==10)
ans++;
return;
}
void dfs(int i){//i为num[]的第i位
if(i==8+1){//i==8+1表示num数组中的前8个已经放置好数字
judge();
return;
}
for(int j=1;j<10;j++){
if(v[j]==0){//判断数字是否使用过
v[j]=1;
num[i]=j;
dfs(i+1);//进行下一个数字的放置
v[j]=0;//回溯,当满足一种全排列后,进行下一种尝试
}
}
}
int main(){
dfs(0);
cout<<ans<<endl;
return 0;
}
3.六角填数
如图所示六角形中,填入1~12的数字。
使得每条直线上的数字之和都相同。
图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?
答案:10
#include <iostream>
using namespace std;
int v[13],a[13]; //v[]标记1~12有没有被使用过,a[]标记12个位置所对应的数
int line[6]; //表示各条线的和
int judge(){//判断每条直线上的数字之和是否都相同
line[0]=a[1]+a[3]+a[6]+a[8];
line[1]=a[1]+a[4]+a[7]+a[11];
line[2]=a[8]+a[9]+a[10]+a[11];
line[3]=a[2]+a[3]+a[4]+a[5];
line[4]=a[2]+a[6]+a[9]+a[12];
line[5]=a[5]+a[7]+a[10]+a[12];
int i;
for(i=1;i<=5; i++) {
if(line[i]!=line[i-1])
return 0;
}
return 1;
}
void dfs(int x){
int j;
if(x==1 || x==2 || x==12){//已知条件则跳过搜索下一个
dfs(x+1);
return ;
}
if(x==12+1){//前12个数已经填满
if(judge()==1)//若满足条件,则输出a[6]的值
cout<<a[6]<<endl;
return ;
}
for(j=1;j<=12;j++){//从1开始找没使用过的数字
if(v[j]==0){
v[j]=1;//标记
a[x]=j;//当前位置存放未使用的数字
dfs(x+1);//进行下一个位置的数字放置
v[j]=0;//回溯
}
}
}
int main(){
//根据题意初始化值
v[1] = 1;
v[3] = 1;
v[8] = 1;
a[1] = 1;
a[2] = 8;
a[12] = 3;
dfs(1);
return 0;
}