DFS 深度优先搜索
第一题 全排列问题
题目: 输出自然数 1 到n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
思路 : 这里用递归每次寻找第d个位置的数,放进去然后标记递归回溯,这样到n个的时就输出。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,i;
bool vis[11];
int ans[11];
void dfs(int d)
{
if(d==n+1)
{
for(int i=1;i<=n;i++)
{
cout<<" "<<ans[i];
}
cout<<endl;
return ;
}
else
{
for(int j=1;j<=n;j++)
{
if(vis[j]==1) continue;
ans[d]=j;
vis[j]=1;
dfs(d+1);
vis[j]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
第二题 组合的输出
题目: 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出 r个元素(不分顺序且 ),我们可以简单地将n 个元素理解为自然数 ,从中任取 个数。现要求你用递归的方法输出所有组合。
思路: 还是用递归,跟前面不同的是取r个位置,每次递归这个位置的前一个位置的下一个数,这样就不用考虑重复了。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,i,r;
bool vis[23];
int ans[23];
void dfs(int d)
{
if(d==r+1)
{
for(int i=1;i<=r;i++)
{
cout<<" "<<ans[i];
}
cout<<endl;
return ;
}
else
{
for(int j=ans[d-1]+1;j<=n;j++)
{
ans[d]=j;
dfs(d+1);
}
}
}
int main()
{
cin>>n>>r;
dfs(1);
return 0;
}
第二题 自然数和分解
题目: 把自然数N分解为若干个大于 0 自然数之和,输出方案数。
思路: 把这个数看成和,然后每次枚举一个数来减和,到和减完,累加器加一。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,sum;
bool vis[110];
int ans[110];
void dfs(int n,int x)
{
if(n==0)
{
sum++;
return ;
}
else
{
for(int j=x;j<=n;j++)
{
dfs(n-j,j);
}
}
}
int main()
{
cin>>n;
dfs(n,1);
cout<<sum<<endl;
return 0;
}
第三题 有重复元素的排列问题
题目: 设R=r1,r2,r3…rn是要进行排列的n个元素。其中元素可能相同。试设计一个算法,列出的R所有不同排列。给定以及待排列的个元素。计算出这个元素的所有不同排列。
思路: 可以先统计每个字母的个数,然后递归每次每个字母,如果这次出完了就不能出这类字母了,这样就不会重复了。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,sum;
char ans[550];
int s[30];
char a[550];
void dfs(int d)
{
if(d==n+1)
{
for(int i=1;i<=n;i++)
{
cout<<ans[i];
}
sum++;
cout<<endl;
return ;
}
else
{
for(int i=1;i<=26;i++)
{
if(s[i]!=0)
{
ans[d]='a'+i-1;
s[i]--;
dfs(d+1);
s[i]++;
}
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[a[i]-'a'+1]++;
}
dfs(1);
cout<<sum<<endl;
return 0;
}
第四题 最佳调度问题
题目: 假设有个n任务由k个可并行工作的机器完成。完成任务需要的时间为ti。试设计一个算法找出完成这个任务的最佳调度,使得完成全部任务的时间最早。
思路: 先从大到小排个序,然后递归每个机器的工作时间,在每次取机器的最大值,如果k个机器都好了,就比较那个最小,如果在途中就比他大了,就直接退出。
代码
#include<bits/stdc++.h>
using namespace std;
int n,i,k,sum,a[60],minn=INT_MAX/3,x[60];
bool cmp(int a,int b)
{
return a>b;
}
void dfs(int d,int maxn)
{
if(maxn>=minn) return ;
if(d==n+1)
{
minn=maxn;
return ;
}
else
{
for(int i=1;i<=k;i++)
{
x[i]+=a[d];
dfs(d+1,max(x[i],maxn));
x[i]=x[i]-a[d];
}
}
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1,cmp);
dfs(1,0);
cout<<minn<<endl;
return 0;
}
第五题 N皇后问题
题目: 在一张N*N的国际象棋棋盘上,放置N个皇后,使得所有皇后都无法互相直接攻击得到,(皇后可以直接攻击到她所在的横行,数列,斜方向上的棋子),现在输入一个整数N,表示在的棋盘上放个N皇后,请输出共有多少种使得所有皇后都无法互相直接攻击得到的方案数。
思路: 跟全排列差不多就是加一个左斜线和右斜线和列的判断。画个图就发现,左斜线是行和列相加,右斜线是行和列相减。
代码
#include<bits/stdc++.h>
using namespace std;
int n,ans;
bool y[100],lx[100],rx[100];
void dfs(int d)
{
if(d==n+1)
{
ans++;
return ;
}
else
{
for(int i=1;i<=n;i++)
{
if( y[i]==1 || rx[i-d+n]==1 || lx[i+d]==1) continue;
y[i]=1; rx[i-d+n]=1; lx[i+d]=1;
dfs(d+1);
y[i]=0; rx[i-d+n]=0; lx[i+d]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
cout<<ans<<endl;
return 0;
}
第六题 迷宫
题目: 在N*N的迷宫内,“#”为墙,“.”为路,“s”为起点,“e”为终点,一共4个方向可以走。从左上角((0,0)“s”)位置处走到右下角((n-1,n-1)“e”)位置处,可以走通则输出YES,不可以走则输出NO。
思路: 每次递归上下左右四个位置,还要记录之前来的位置,好要判断是否是墙或者超界了,直到搜到终点。
代码
#include<bits/stdc++.h>
using namespace std;
int n,ans,k;
char a[20][20];
bool f,vis[20][20];
bool can(int x,int y)
{
if(a[x][y]=='#') return 0;
if( !(x>0&&x<=n) || !(y>0&&y<=n) ) return 0;
return 1;
}
void dfs(int x,int y)
{
if (f == 1) return;
if(a[x][y]=='e')
{
cout<<"YES"<<endl;
f=1;
return ;
}
else
{
if(can(x+1,y) && vis[x+1][y]==0) //右
{
vis[x+1][y]=1;
dfs(x+1,y);
}
if(can(x,y+1) && vis[x][y+1]==0) //下
{
vis[x][y+1]=1;
dfs(x,y+1);
}
if(can(x,y-1) && vis[x][y-1]==0) //上
{
vis[x][y-1]=1;
dfs(x,y-1);
}
if(can(x-1,y) && vis[x-1][y]==0) //左
{
vis[x-1][y]=1;
dfs(x-1,y);
}
}
}
int main()
{
cin>>k;
while(k--)
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
}
dfs(1,1);
if(f==0) cout<<"NO"<<endl;
}
return 0;
}