最基础的“穷竭搜索”

递归函数

递归函数:在一个函数中再次再次调用该函数自身的行为叫做递归。如下为公式n! =n * (n - 1)!

int fact (int n) {

    if(n== 0) return 0;

    return fact n*fact(n - 1);

}

if(n == 0) 这个限制条件使递归有结束的出口,如果没有这一个条件,程序将无限制的运行下去。


接下在看斐波那契数列a₀= 0;a₁=1;n=an-1+an-2(n>1)

int fib( int n){

    if(n<= 1) return n;

    return fib(n - 1) + fib(n -2 );

}

实际上这个函数时间性能很差,通过分析可以发现,在递归过程中 有很多重复的递归过程,如果能将重复的子递归结果存储起来,那么就不需要再次计算。

int memo[MAX_N +1];

int fib(int n){

    if(n<=1) return n;
    if(memo[n] != 0) return memo[n];
    return memo[n]=fib(n-1) + fib(n - 2);
}


栈是支持push和pop的两种操作的数据结构。push是从栈顶端放入数,pop是从栈顶端取出数。这种操作被称为后进先出方式。

 C++标准库中,stack :: pop移除最顶端的数据 ; stack::top访问最顶端数据。

#include<iostream>
#include<stack>
#include<cstdio> 

using namespace std;

int main(){
	
	
	stack<int> s;
	s.push(1);
	s.push(2);
	s.push(3);
	
	printf("%d\n", s.top());
	s.pop();
	printf("%d\n", s.top());
	s.pop();
	printf("%d\n", s.top());
	s.pop();
	return 0;
} 


队列


队列是一种先进先出的数据结构。

#include<iostream>
#include<queue>
#include<cstdio> 

using namespace std;

int main(){
	
	
	queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	
	printf("%d\n", q.front());//访问队尾 
	q.pop();                  //移除队尾 
	printf("%d\n", q.front());
	q.pop();
	printf("%d\n", q.front());
	q.pop();

	return 0;
} 

深度优先搜索DFS

深度优先搜索,它从某个状态开始,不断的转移状态直到无法转移,然后回退到前一个状态,继续转移到其他状态,如此不断重复,直到找到最终解。根据深度优先搜索的特点,可以采用递归函数来实现深度优先搜索。


例题:部分和问题:

给定整数 1 a2 、…… 、an,判断是否可以从中选出若干数,使它们的和恰好为K。

 限制条件:1 n 20 -108ai 108-108k108

输入样例:n=4 

                 a={1,2,4,7}

                   k=13

输出样例:Yes(13 = 2+4+7)

//给出整数a1~an,判断是否可以从中找出若干,使它们的和为k

#include<iostream>

using namespace std;
int n,k, * a;  //n为输入数据个数,k为所要的求和,a*为输入的数据数组

void init(){
	cin>>n;
	a=new int[n];
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	cin>>k;
}

bool dfs(int i,int sum){
	if(i==n) return sum=k;
	if(dfs(i+1,sum) ) return true;
	if(dfs(i+1,sum+a[i]))return true;
	return true;
}

int main(){
	init();
	if(dfs(0,0)) cout<<"Yes";
	else cout<<"No";
	return 1;
}

该算法其实很简单,如上,dfs只用了五行代码。

假如我们输入的数组长度为5,它们分别为1、2、3、4、5,求它们任意和的总情况有多少种呢?

我们可以这样分析,可以把他作为概率论的问题,从中任意挑几个数,1出现或不出现有两种情况,2出现或不出现有两种情况,……。说以总情况为2^5种。

说以,我们可以以此来画一个二叉树,每层节点代表一个数,左边为不出现,右边为出现:

                                                        

                                                        

宽度优先搜索

宽度休闲搜索与深度优先搜索的不同之处在于搜索顺序不同。宽度优先搜索可以想象为按行搜索,深度优先搜索可以看作按列搜索。

问题

迷宫最短路径问题

给定一个大小为N*M的迷宫。迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四格的通道移动。请求从起点到终点所需的最小步数。请注意,本题假定从起点一定可以移动到终点。

限制条件:N*M<=100

输入样例:    N=10,M=10;

#S######.#
......#..#
.#.##.##.#
.#........
##.##.####
....#....#
.#######.#
....#..... 
.####.###.
....#...G#

输出: 22

宽度优先搜索需要使用队列,问题中使用dis[N][M]来保存路径长度;初始化时用充分大的常数INF来初始它。

因为向四个方向移动,说以可以设置一个结构struct Move{int dx,int dy};

移动方向的坐标轴为:


#include <iostream>
#include<queue>
#define MAX_N 100
#define MAX_M 100
using namespace std;
struct Move{
	int dx;
	int dy;
};
const static int INF=10000000;
typedef pair<int,int> P;
char maze[MAX_N][MAX_M];
int dir[MAX_N][MAX_M+1];
int N,M; //迷宫大小
int sx,sy;//起点坐标
int gx,gy;//终点坐标
Move moved[]={{1,0}, {0,1}, {-1,0}, {0,-1} }; //移动方向:右,下,左,上

int bfs(){
	queue<P> que;
	for(int i=0;i<N;i++){
		for(int j=0;j<M;j++){
			dir[i][j]=INF;
		}
	}
	que.push(P(sx,sy));//起点加入队列
	dir[sx][sy]=0;//将起点的距离设置为0

	while(que.size()!=0){
		P p=que.front();
		que.pop();
		if(p.first==gx && p.second == gy)
			break;

		//四个方向循环
		for(int i=0;i<4;i++){
			int nx=p.first+moved[i].dx;
			int ny=p.second+moved[i].dy;
			//判断该位置是否可到达,且是否已经到过
			if(nx>=0 && nx<N && ny>=0 && ny<M && maze[nx][ny] != '#' && dir[nx][ny] ==INF){
				que.push(P(nx,ny));
				dir[nx][ny]=dir[p.first][p.second]+1;
			}
		}
	}
		return dir[gx][gy];
}

void init(){
	cin>>N>>M;
	for(int i=0;i<N;i++){
		for(int j=0;j<M;j++){
			cin>> maze[i][j];
			if(maze[i][j]=='S') {sx=i,sy=j;}
			if(maze[i][j]=='G') {gx=i,gy=j;}
		}
	}
}

int main(){
	init();
	cout<<bfs()<<endl;
	return 1;
}









猜你喜欢

转载自blog.csdn.net/qq_28120673/article/details/80781806