DFS(深度优先搜索),不断向前遍历,当遍历至尽头后回溯,向下一个分支继续遍历。
图示:
模板1:递归实现排列型枚举
输出n的全排列。
DFS每一层选一个数,递归n层即可。
图示:
代码模板如下:
#include<iostream>
using namespace std;
const int N=20;
int path[N];//记录一下路径
bool st[N];//状态数组
int n;
void dfs(int u){
if(u==n){//如果到底层外就return并且输出路径
for(int i=0;i<n;i++)cout<<path[i]<<" ";
cout<<endl;
return;
}
for(int i=0;i<n;i++){//寻找分支往下
if(!st[i]){//如果这个点没搜过
st[i]=true;//选择这条分支
path[u]=i+1;
dfs(u+1);//下一层
st[i]=false;//恢复状态
}
}
return;
}
int main(){
cin>>n;
dfs(0);
return 0;
}
模板2:递归实现指数型枚举
从 1∼n 这 n个整数中随机选取任意多个,输出所有可能的选择方案,输出无需考虑顺序。
每个数有选和不选两个情况,代表两个分支,每一层选一个数。
代码如下:
#include<iostream>
using namespace std;
const int N=20;
bool st[N];
int n;
void dfs(int u){
if(u==n){//如果到底
for(int i=0;i<n;i++)
if(st[i])cout<<i+1<<" ";
cout<<endl;
return;
}
st[u]=true;//选
dfs(u+1);
st[u]=false;//恢复状态
dfs(u+1);//不选
}
int main(){
cin>>n;
dfs(0);
return 0;
}
例题:
1.叶子节点
给定一棵 n个节点的树,节点编号 1∼n,1 号节点为树的根节点;
每个节点要么是黑色的,要么是白色的;
对于一个叶子节点,如果从该节点到根节点的路径(包括两端节点)中有超过m个黑色节点连续的排列在一起,则称该节点为无效叶子节点;
有效叶子节点数量 = 总叶子节点数量 - 无效叶子节点数量;
请你统计有效叶子节点的个数。
输入格式:
第一行包含两个整数 n,m;
第二行包含 n个整数 a1,a2,…,an,其中 ai表示第 i个节点的颜色,1 表示黑色,0 表示白色;
接下来 n−1 行,每行包含两个整数 x,y,表示节点 x 和节点 y 之间存在一条无向边;
保证输入给定的是一棵树。
输出格式:
一个整数,表示有效节点的个数。
DFS,参数传入上一个节点是否为有效节点,截止到上一个节点的路径上的黑色节点个数,当前的节点编号,当前节点的父节点的编号(输入的为无向边)。
代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+10;
int h[N],w[N],e[2*N],ne[2*N],idx=0;
int n,m;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int pa,int x,int cnt,bool st){
if(w[x])cnt++;
else cnt=0;
if(cnt>m)st=false;
int sons=0,res=0;
for(int i=h[x];i!=-1;i=ne[i]){
int j=e[i];
if(j==pa)continue;
res+=dfs(x,j,cnt,st);
sons++;
}
if(st&&sons==0)res++;
return res;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>w[i];
memset(h,-1,sizeof h);
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
cout<<dfs(-1,1,0,true);//父节点,当前节点,黑色个数,是否合法
return 0;
}