算法入门 | 初出茅庐——初级篇

算法入门 | 初出茅庐——初级篇

   *本博客内容均可参考教材《挑战 程序设计竞赛》 第二版*
   本博客内容仅供学习交流使用,禁止用于商业目的
   ***未经允许禁止转载***

最基础的*“穷竭搜索”*

穷竭搜索即所谓的暴力,是将所有的可能性罗列出来,在其中寻找答案的方法。但是暴力搜索也有一定的技巧性,这里
我们主要介绍深度优先搜索(dfs)和广度优先搜索(bfs),这是ACM竞赛中很基础,很常用算法。

1.递归函数

递归函数是深度优先搜索(dfs)的基础,所以本文从递归开始谈起。

/ / 在一个函数中再次调用该函数自身的行为叫做递归,这样的函数被称为递归函数。

 ~~递归函数比较简单,所以我就举一个栗子,看完就能理解了~~ 
///  斐波那契数列 
#include 
using namespace std;
/*
	递归将一个大问题拆分成了和自身类似但规模较小的问题,所以递归次数多是难免会进行重复的运算导致时间超限
	对此我们可以使用数组储存重复的计算结果降低时间复杂度 
*/ 
int memory[15];  //储存结果 
int fib (int n)   //递归函数
{
     
	if(n<=1) return n;
	if(memory[n]!=0) return memory[n];
	return memory[n] = fib(n-1) + fib(n-2);
} 
int main()
{
     	
	memset(memory,0,sizeof(memory));
	int n;
	while(scanf("%d",&n)!=EOF)
	{
     
		printf("%d\n",fib(n));
	}
	return 0;
}

2.栈

	栈(Stack)是支持push和pop两种操作的数据结构。  深搜的思想和栈十分类似
		最后入栈的数据可以最先被取出,这种行为被叫做LIFO:	Last In First Out
	//  C++标准库中有关于栈操作的基本函数   **头文件  # include **
		  关于栈的函数在C++标准库中还有很多,有兴趣的可以自己去搜索,用起来很方便的
	    	//  push是在栈的顶端放入一组数据的操作
	        //  pop是从顶端取出一组数据的操作
	        //  top访问栈顶的数据(stack::top,这个操作被称为peek)

继续来个栗子

#include 
#include 
#include 
using namespace std;
int main()
{
     	
	//声明一个栈   stack <栈中数据的类型  例如int char float等等>  栈名
	stack<int> s;  //声明储存int类型数据的栈
	s.push(1);    //将int 变量1压入栈中   栈顶数据为 1
	s.push(2);    //将int 变量2压入栈中   栈顶数据为 2
	s.push(3);    //将int 变量3压入栈中   栈顶数据为 3 
	printf("%d\n",s.top());   //调用top函数访问栈顶数据将其输出
	s.pop();      //将栈顶数据(3)取出 此时栈顶为 2  
	printf("%d\n",s.top());
	s.pop();      //将栈顶数据(2)取出 此时栈顶为 1
	printf("%d\n",s.top());
	s.pop();
	
	return 0;
}

算法入门 | 初出茅庐——初级篇_第1张图片

3.队列

	队列与栈一样支持push和pop操作  不同的是pop取出的是最底端的元素,也就是说最先放入的元素最先取出(这种
	行为被称为FIFO:First In First Out)       **广度优先搜索借助了队列来实现**
	C++同样有丰富的队列函数,头文件 # include 
	区别于栈的是C++中函数queue::front 访问最底端数据

代码类似于栈(有略微的区别)

#include 
#include 
#include  
using namespace std;
int main()
{
     
	queue<int> s;  //声明储存int类型数据的栈
	s.push(1);
	s.push(2);
	s.push(3);
	printf("%d\n",s.front());
	s.pop();
	printf("%d\n",s.front());
	s.pop();
	printf("%d\n",s.front());
	s.pop();
}

运行结果
算法入门 | 初出茅庐——初级篇_第2张图片
4.深度优先搜索(dfs)

	它从某个状态开始,不断地转移状态直到无法转移,然后回退到前一步的状态,继续转移到其他状态,如此不断重复,
直到找到最终的解。
  • 部分和问题
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
题目描述 
 给定整数A1、A2、···、An,判断是否可以
 从中选出若干数,使他们的和恰好为k。 
 !!!限制条件
 	·1<=n<=20 
	·-10e8<=Ai<=10e8 
	·-10e8<=k<=10e8
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
样例一 
	输入 
		 n = 4
		 a = {
     1,2,4,7}
		 k = 13
	输出
		 Yes (13 = 2 + 4 + 7) 
样例二 
	输入
		 n = 4
		 a = {
     1,2,4,7}
		 k = 15 
	输出 
		 No
#include 
using namespace std;
int n,k,a[25];
	bool dfs (int i,int sum)
	{
     
		//搜索边界 判断sum等于k 
		if(i==n) return sum==k;
		//sum不加a[i]; 
		if(dfs(i+1,sum)) return true;	
		//sum加上a[i]; 
		if(dfs(i+1,sum+a[i])) return true;
		//无论加不加sum都不能等于k的时候返回false 
		return false; 
	}
	void solve()
	{
     
		if(dfs(0,0)) printf("Yes\n");
		else printf("No\n");
	}
	int main()
	{
     
		scanf("%d",&n);
		memset(a,0,sizeof(a));
		for(int i=0;i<n;i++) scanf("%d",&a[i]);
		scanf("%d",&k);
		solve();
		return 0;
	}

——————————————我是一个华丽丽的分割线————————————————

  • Lake Counting (POJ No.2386)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
题目描述 
 有一个大小为N*M的园子,雨后积起了水。八连通的积水
 被认为是链接在一起的。请求出园子里总共有多少水洼?
 (八连通指的是下图中相对W的*的部分)
 	***
	*W*
	***
 !!!限制条件
 	·N,M <= 100 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
样例一
	输入
		 N = 10 , M = 12
		 园子如下图('W'表示积水,'*'表示没有积水)
		 W........WW.
		 .www.....WWW
		 ....WW...WW.
		 .........WW.
		 .........W..
		 ..W......W..
		 .W.W.....WW.
		 W.W.W.....W.
		 .W.W......W.
		 ..W.......W.
	输出
		 3
//我们考虑从图中的第一个位置搜索,如果遇到水坑W,便执行dfs将水坑变为平地,然后从水坑的位置开始检查其周围的八个方向,如果有水坑就继续执行dfs。直到搜索完整张地图,便可以得到所有的水坑总数
#include 
using namespace std;
int n,m,ans;   	//n*m为园子的范围
char yz[110][110];  //储存园子的状态
void dfs (int x,int y)    //将水坑变为平地,然后搜索水坑周围,继续dfs
{
     
	yz[x][y]='.';    //讲水坑 ‘W’ 变为‘.’
	
	for(int move_x=-1;move_x<=1;move_x++)
	{
     
		for(int move_y=-1;move_y<=1;move_y++)
		{
     
			//使用两重循环,遍历水坑周围八个方向
			int now_x=move_x+x,now_y=move_y+y;  	//(now_x,now_y)即为周围的坐标
			//判断:如果左边还在院子内并且当前作别为W,则继续调用dfs搜索当前位置。
			if(now_x<n && now_x>=0 && now_y<m &&now_y>=0 && yz[now_x][now_y]=='W') dfs(now_x,now_y);   
		}
	}
	return ;
}
int main()
{
     	
	ans=0;   //水坑总数
	scanf("%d%d",&n,&m);
	getchar(); 
	for(int i=0;i<n;i++)
	{
     
		for(int j=0;j<m;j++) scanf("%c",&yz[i][j]);
		getchar();
	 } 
	for(int i=0;i<n;i++)
	{
     
		for(int j=0;j<m;j++)
		{
     
			//因为对于每一个水坑,第一次遇到时会将其周围的水坑全部标记为平地。所以遇到水坑的次数便为总的水坑数
			if(yz[i][j]=='W')
			{
     
				dfs(i,j);
				ans++;
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

未完待续······

你可能感兴趣的:(C/C++,基础算法,搜索,暴力,ACM入门,算法入门)