深度优先算法
前言
深度优先是遍历图的一种算法,对于我们解蓝桥杯的题目,我们可以不懂图是什么,他是怎样遍历图。对于蓝桥杯来说他就是一种暴力的算法,就是一种将很多层for循环化成递归的方法,用于爆破来解一些问题。他是蓝桥杯的必考考点,希望大家务必掌握,这种题目一般有俩种类型,排列和组合。下面我们通过几道题目来了解这个算法。
排序例题
题目背景
猪猪hanke得到了一只鸡
题目描述
猪猪Hanke特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke吃鸡很特别,为什么特别呢?因为他有10种配料(芥末、孜然等),每种配料可以放1—3克,任意烤鸡的美味程度为所有配料质量之和
现在,Hanke想要知道,如果给你一个美味程度,请输出这10种配料的所有搭配方案
输入输出格式
输入格式:
一行,n<=5000
输出格式:
第一行,方案总数
第二行至结束,10个数,表示每种配料所放的质量
按字典序排列。
如果没有符合要求的方法,就只要在第一行输出一个“0”
11
10
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 2 1 1
1 1 1 1 1 1 2 1 1 1
1 1 1 1 1 2 1 1 1 1
1 1 1 1 2 1 1 1 1 1
1 1 1 2 1 1 1 1 1 1
1 1 2 1 1 1 1 1 1 1
1 2 1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1 1 1
分析
这题最容易想的方法就是用10重循环来解这道题目了,如果不嫌麻烦的话这道题目用10重for循环是肯定可以写出来的,但是我们往往是将for循环改成递归来解这道题目的,那么不知不觉中我们用递归解的这种算法就相当于遍历图的深度优先算法了。下面上代码
package 烤鸡;
public class Main {
public static int[] nums=new int[11];
public static boolean cheak(){
int sum=0;
for(int i=1;i<11;++i)
sum+=nums[i];
if(sum==11)
return true;
return false;
}
public static void dfs(int level){
if(level==11){
if(cheak()){
for(int i:nums)
System.out.print(i+" ");
System.out.println();
}
return;
}
for(int i=1;i<=3;++i){
nums[level]=i;
dfs(level+1);
}
}
public static void main(String[] args) {
dfs(1);
}
}
代码分析
其实对于大家现在只要知道他是将9层for变成递归的一种算法就行了,一般这种题目都是一个dfs递归函数用来遍历所有可能的情况,还有一个check函数来筛选出适合的情况解。
组合例题
题目描述
已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:
3+7+12=22
3+7+19=29
7+12+19=38
3+12+19=34。
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数:3+7+19=29)。
输入输出格式
输入格式:
键盘输入,格式为:
n , k (1<=n<=20,k<n)
x1,x2,…,xn (1<=xi<=5000000)
输出格式:
屏幕输出,格式为:
一个整数(满足条件的种数)。
4 3
3 7 12 19
1
分析
对于上题来说1 1 1 1 1 1 1 1 1 2 和1 1 1 1 1 1 1 1 2 1是俩种不同的解,但是这题3,7,12和7,3,12就是同一种解了。这就是排列与组合的不同了,信息排列与组合大家高中都学过这里就不多说了。那么对于这题我们该怎么解呢?我们可以先设一个起始的位置,对于遍历到后面的节点我们就可以不遍历了,比如起始位置是7那么后面他就不会遍历到3了,这样就不会出现7,3,12 的情况了,下面上代码
package day7;
import java.util.Scanner;
public class 选数_组合 {
public static int[] nums=new int[100];//存输入的数
public static int[] dp=new int[100];//递归得到的数
public static int start,n,m,count;//子函数与主函数都要用到的全局变量
public static boolean[] visited=new boolean[100];//每个数只能取一次
public static boolean cheak(){
int sum=0;
for(int i=1;i<=m;++i){
sum+=dp[i];
}
int sqrt=(int)Math.sqrt(sum);
for(int i=2;i<=sqrt;++i)
if(sum%i==0)
return false;
return true;
}
public static void dfs(int start,int level){//start为标志位
if(level==m+1){
if(cheak()){
for(int i=1;i<=m;++i){
System.out.print(dp[i]+" ");
}System.out.println();
count++;
}
return;
}
for(int i=start;i<=n;++i){
if(!visited[nums[i]]){
dp[level]=nums[i];
visited[nums[i]]=true;
dfs(i+1,level+1);
visited[nums[i]]=false;
}
}
}
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
n=input.nextInt();
m=input.nextInt();
for(int i=0;i<1000;++i)
for(int j=0;j<1000;++j)
{}
for(int i=1;i<=n;++i)
nums[i]=input.nextInt();
dfs(1,1);
//System.out.println(count);
}
}
代码分析
对于这道题目,他就是组合的一种,他相对于排列,代码的其他部分都是一样的,我们只需要在代码中加一个起始位置(start)就可以了。
总结
对于这类题都是遍历出来所有可能的值,然后在遍历的值里面选取出自己想要的值即可。说白了就是一种爆破算法,题目的代码也都是一个dfs递归函数,一个cheak函数,基本都是这个模板,所以也很简单,只要有大量的题目练习这类题还是没有问题的,蓝桥杯必考哦,希望大家务必掌握。下面附上几道习题。
https://download.csdn.net/my