DFS 是一个常用的算法。
那 DFS 是干嘛的呢?
我们看这道题目 洛谷 P1706
这道题该怎么做呢?
多重循环?要 n 重循环?(不是)
照这么说,假如 n = 3 时:
//非正确代码!
#include<bits/stdc++.h>
using namespace std;
int main()
{
for(int i=1;i<=3;i++)
{
for(int j=1;j<=3;j++)
{
if(i==j) continue;
for(int k=1;k<=3;k++)
{
if(i==k||j==k) continue;
printf("%5d%5d%5d\n",i,j,k);
}
}
}
return 0;
}
很快,你就会发现,这样不行!
n = 4 时,代码不一样了;
n = 5 时,代码又不一样了;
······
而且这样时间复杂度很高,O(n^n)!
这时候,我们就要用到 DFS
在第一步时,有 n 种抉择;
第二步时,有 n-1 种抉择;
······
那么,我们就可以把它想象成一个递归(本来就是递归)
在面对 n 种抉择时,每一种都可以进入下一层递归;
在面对 n-1 种抉择时,每一种都可以进入下一层递归;
在面对 n-2 种抉择时,每一种都可以进入下一层递归;
·······
当剩下 0 种选择时,便是递归终止之时。
但是我们怎么把抉择的数量从 n 减道 n-1 呢?
我们可以用一个 bool 型的数组进行标记已经用过的数字;
如果我们把所有的数字都标记完了,岂不是没有数字可以用了?
这时候,我们就要用回溯了,把用好了的数字的标记删除。
并且,记录下已用数字,便于输出。
根据以上所述,我们可以得出主要代码:
int n;
int a[1001];
bool vis[1001]; //这便是用于标记的数组
void dfs(int step)
{
if(step==n+1) //一旦所有数字都用到了
{
for(int i=1;i<=n;i++)
printf("%5d",a[i]); //保留5个场宽输出(printf("%5d"))
printf("\n");
return ; //返回上层
}
for(int i=1;i<=n;i++)
{
if(vis[i]!=true) //如果没有被标记
{
a[step]=i; //记录下这个数是什么
vis[i]=true; //标记
dfs(step+1); //去下一层
vis[i]=false; //用完后取消标记
}
}
}
DFS 的时间复杂度虽然比较高,为O(n!),但是比起 n 重循环(O(n^n)),已经优化很多了。
最后给出全部代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[1001];
bool vis[1001];
void dfs(int step)
{
if(step==n+1)
{
for(int i=1;i<=n;i++)
printf("%5d",a[i]);
printf("\n");
return ;
}
for(int i=1;i<=n;i++)
{
if(vis[i]!=true)
{
a[step]=i;
vis[i]=true;
dfs(step+1);
vis[i]=false;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
a[i-1]=i;
dfs(1);
}
初学者理解起来会有点困难(单纯指刚刚接触 DFS 的初学者)
但是,算法本来就大部分就靠学者本身的理解,多多理解,自己便能学会。
那么,祝所有看这篇博客的小伙伴们代码能力越来越强。
(这篇博客是蒟蒻的第一篇 CSDN 的博客,望大家别踩)