当我初入洛谷的DFS模块,我遇到的第一道题,就是经典的八皇后问题
从学完数据结构之后再没接触过算法,这次是为了备战蓝桥杯才去学算法,dfs不懂的我,陷入了巨大的沉思,只能在CSDN上摸索
但是!!!哥们我只想要dfs的模板啊,我怎么能想到你能想到的呢?慢慢悟吧,没办法了(哎)
接下来,进入正题
所以我选择了从一个最简单的题入手,这个题是我自己想的哈,就是从1 2 3 4 5里面任挑3个然后输出,即高中数学的A(5/3)
首先,我们需要一个全局变量n,和一个填入了(1,2,3,4,5)的数组,当然数组也要是全局的
还有一个全局的vis数组,用来标记是否访问过这个元素(这个下面会提到)
b数组用来存储答案,cns数组用来算总的结果数,不出意外的话:cns=6x5x4=60
int n,a[100],vis[100],b[100],cns;
然后就是简单的输入,这里不多赘述
int main()
{
cin>>n;
for(int i=1;i<=n;i++) //这里建议大家从1开始存哈,后面的step会方便一点
{
cin>>a[i];
}
dfs(1); //这个先别管,先看下面
cout<<cns;
}
然后是核心要点:怎么去写dfs的函数呢?上模板!
①定义一个 step 变量,用来表示我们现在选的是第几个数 ( 比如dfs(1),那么代表着我现在正在选第1个数,还不知道花落谁家 )
void dfs(int step) //模板,可能不同的题会需要多个参数
②接着存数进b数组,我们到时候要输出,下面的这几行代码都是模板,请牢牢记住
for(int i=1;i<=n;i++) //注释一如下
{
if(vis[i]) // 模板:判断vis数组中这个数有没有被选过,如果选过,就continue
{
continue;
}
b[step]=a[i]; // 模板:存入最终要存输出的答案的数组
vis[i]=1; // 已经选中它啦,所以要把vis数组的第i个置为1,表示我已经选过了
dfs(step+1); //选下一个啦
vis[i]=0; // 这里也是我当初学的时候很不理解的一个点,但是我后来悟道了,注释二
}
注释一:状态:你的有关step的最终要存输出的答案的数组里最终要存的是什么?如果是(1,2,3,4,5)的a数组任选一个,那么就是要选择关于a数组的循环,这里选择从(1,n)循环遍历a数组
注释二:这里为什么要重置呢?建议和下面的一起看哈!如果从程序的角度来说:
(1)我们先选了1,此时有关1的还有一个最外层的循环啊,就是(1,5)的这个循环
(2)然后深搜dfs(2),此时注意再次进了上面这个循环,因为vis【1】=1,所以不选,继续循环,i=2,vis【2】=0,所以可选,选中2
(3)同理继续深搜,vis【1】=vis【2】=1,vis【3】=0,选中3,注意:此时我们已经选了3个数了
(4)继续深搜:此时为dfs(4),选了3个数了,所以我们需要输出并且返回上一个选中3的循环,那么下一步就应该是vis【3】置为0
(5)这个时候你就会想啦,为啥要把它置为0呢?这是因为:3已经遍历过了,按道理来说,下一个是(1,2,4),但是你已经选中了(1,2,3)啦,你不丢掉一个,怎么选下一个呢,那么此时丢掉3,即vis【3】置为0,此时我们才2个数,还是可以寻找第3个数的
(6)那么这时,在我们最深层的循环中,会接着跳过3去遍历4
《可以画一个图帮助理解一下》
③什么时候return,输出呢,当然是选了3个数的时候啦,即step==4
if(step==4) //模板:填写输出阈值
{
for(int i=1;i<4;i++) //输出
{
printf("%d ",b[i]);
}
cns++; //看题目要求,可加可不加,如果题目需要先输出总数,再输出各种情况,那么就需要开个二维数组把情况都存起来,暂时就不能输出
printf("\n");
return ;
}
总代码如下
#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
using namespace std;
int n,a[100],vis[100],b[100],cns;
void dfs(int step)
{
if(step==4)
{
for(int i=1;i<4;i++)
{
printf("%d ",b[i]);
}
cns++;
printf("\n");
return ;
}
for(int i=1;i<=n;i++)
{
if(vis[i])
{
continue;
}
b[step]=a[i];
vis[i]=1;
dfs(step+1);
vis[i]=0;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
dfs(1);
cout<<cns;
}
再来一题,是我继续学习dfs的时候碰到的和上面的差不多的题,就是高中数学的C(5/3)啦 :洛谷P1157
大家可以先别看,先自己试着写一下,我们难度慢慢递进
你可以发现,完完全全按模板来写就行
#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
#include <iomanip>
using namespace std;
int n,r;
int a[25],visit[25],b[25];
void dfs(int step){
if(step==r+1){
for(int i=1;i<=r;i++){
cout<<setw(3)<<b[i];
}
cout<<endl;
return;
}
for(int i=1;i<=n;i++){
if(visit[i]==1){
continue;
}
if(step==1 || i>b[step-1]) { //主要的区别就在这里,我们可以通过添加一些限制条件来控制我们的选择
b[step] = i;
visit[i] = 1;
dfs(step + 1);
visit[i] = 0;
}
}
}
int main(){
cin>>n>>r;
for(int i=1;i<=n;i++){
a[i]=i;
}
dfs(1);
}
继续深入,看题(洛谷P2089 烤鸡)
大家可以先别看,先自己试着写一下,注意之前注释一中提到的状态,我们的最终数组里究竟要存的是什么?
#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
using namespace std;
int n;
int ans;
int a[15],b[10000][15],visit[15];
void dfs(int step,int sum){
if(step>10){ //超出极限步数后就应要输出
if(sum==n){ //进一步限制能输出的情况
ans++;
for(int i=1;i<=10;i++){
b[ans][i]=a[i]; //需要先存入二维数组中,因为要先输出ans的值
}
}
return;
}
for(int i=1;i<=3;i++){ //状态:你的step数组里最终要存的是什么?如果是(1,2,3)调料的克数,那么就是有关1-3的循环,如果是(1,2,3,4,5)中的任意一个数,那么就是a[i]循环该数组然后存入
if(visit[step]==1){
continue;
}
if(sum+i<=n){
a[step]=i;
visit[step]=1;
dfs(step+1,sum+i);
a[step]=0;
visit[step]=0;
}
}
}
int main(){
cin>>n;
if(n<10 || n>30){
cout<<"0";
}else{
dfs(1,0);
cout<<ans<<endl;
for(int i=1;i<=ans;i++){
for(int j=1;j<=10;j++){
cout<<b[i][j]<<" ";
}
cout<<endl;
}
}
}
再次深入——八皇后(洛谷P1219)图在文章开始
区别在于:记录遍历的数组vis增加,从1个变成了4个
#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
using namespace std;
int n,j=2,cns;
int a[100],b[100],c[100],d[100];
int ans[100];
void dfs(int step){
if(step>n){
if(j>=0){
for(int i=1;i<=n;i++){
cout<<ans[i];
if(i!=n){
cout<<" ";
}
}
j--;
cout<<endl;
}
cns++;
return;
}
for(int i=1;i<=n;i++){ // i代表列
if(!b[i] && !c[i+step] && !d[i-step+n]){
ans[step]=i;
b[i]=1;
c[i+step]=1;
d[i-step+n]=1;
dfs(step+1);
b[i]=0;
c[i+step]=0;
d[i-step+n]=0;
}
}
}
int main(){
cin>>n;
dfs(1);
cout<<cns;
}
“算法虐我千百遍,我待算法如初恋”,加油,希望未来我们顶峰相见!!!收藏一下吧嘿嘿