搜索 - DFS
思路:
DFS 即 深度优先搜索。
搜索方式:以树形图为例,如果当前的节点满足条件,则继续搜索他的子节点;如果不满足条件,则向上回溯到前一个还有没被访问到的邻接点的父节点
,再向下搜索它的子节点。就是不撞南墙不回头,解释不清楚,上代码。
伪代码 / 模板:
void dfs(状态A){
if(A不合法)
return ;
if(A为目标状态)
输出;
if(A不为目标状态)
dfs(A+a);
}
入门题:AcWing842. 排列数字
题面:【传送门】
思路:
用st[i]
标记i
是否在当前排列中;
用hp[i]
记录当前排列中第i
位的数;
k
表示当前排序中由几个数字
AC代码
#include<iostream>
using namespace std;
int n;
int hp[15];
bool st[15];
void dfs(int k){
if(k==n){
for(int i=0;i<n;i++)
cout<<hp[i]<<" ";
cout<<endl;
return ;
}
for(int i=1;i<=n;i++){
if(!st[i]){
hp[k]=i;
st[i]=1;
dfs(k+1);
st[i]=0;
}
}
}
int main(){
cin>>n;
dfs(0);
return 0;
}
模板题:AcWing843. n-皇后问题
题面:【传送门】
思路:
从第一行
开始遍历 (用行数遍历,所以每一行上不会重复摆) ,所以只需要3个数组:
st[i]
标记第i
行是否有棋子;
dj[i]
标记第i
条对角线是否有棋子;
fdj[i]
标记第i
条反对角线是否有棋子;
( 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 ) |
对于坐标(i,j)
:
如果
i
1
+
j
1
=
=
i
2
+
j
2
i_{1}+j_{1} == i_{2}+j_{2}
i1+j1==i2+j2,则两点在一条反对角线 (右下) 上。
如果
i
1
−
j
1
=
=
i
2
−
j
2
i_{1}-j_{1} == i_{2}-j_{2}
i1−j1==i2−j2,则两点在一条正对角线 (左上) 上。
判断k==n+1
,遍历到了最后一行就输出棋盘。
AC代码
#include<iostream>
using namespace std;
int n;
int fdj[30],dj[30],st[15];
char mp[15][15];
void dfs(int k){
if(k==n+1){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cout<<mp[i][j];
cout<<endl;
}
cout<<endl;
return ;
}
for(int i=1;i<=n;i++){
if(!st[i]&&!fdj[i+k]&&!dj[n-i+k]){
mp[k][i]='Q';
st[i]=fdj[i+k]=dj[n-i+k]=1;
dfs(k+1);
mp[k][i]='.';
st[i]=fdj[i+k]=dj[n-i+k]=0;
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
mp[i][j]='.';
dfs(1);
return 0;
}
模板题:AcWing846. 树的重心
DFS并不直接返回答案,而是在每次更新中迭代一次答案。
题面:【传送门】
思路:
见AC代码注释。
AC代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N=100005;
int n,ans=N;
int h[N],e[N*2],ne[N*2],idx; //邻接表
bool st[N]; //访问标记
void add(int x,int y){
e[idx]=y;
ne[idx]=h[x];
h[x]=idx++;
}
int dfs(int k){
int cnt=0; //删掉某个节点后 最大的联通子图的节点数
st[k]=1;
int sum=1; //记录 k节点的子树的节点数(包括k点)
for(int i=h[k];i!=-1;i=ne[i]){ //访问 k的子节点
int hp=e[i];
if(!st[hp]){
int h=dfs(hp); //k的以hp为节点的子树的节点数
cnt=max(cnt,h); //比较得最大
sum+=h; //k的节点数=他的子树的节点数的和
}
}
cnt=max(cnt,n-sum); //总的节点数-根的子树节点数 即为剩余的连通节点数
ans=min(cnt,ans); //最大值最小
return sum;
}
int main(){
//freopen("D:\\in.txt","r",stdin);
memset(h,-1,sizeof h);
cin>>n;
int x,y;
for(int i=0;i<n-1;i++){
scanf("%d%d",&x,&y);
add(x,y); //以有向图的形式存储无向图
add(y,x); //边数是两倍
}
dfs(1);
cout<<ans;
return 0;
}