*本博客内容均可参考教材《挑战 程序设计竞赛》 第二版*
本博客内容仅供学习交流使用,禁止用于商业目的
***未经允许禁止转载***
穷竭搜索即所谓的暴力,是将所有的可能性罗列出来,在其中寻找答案的方法。但是暴力搜索也有一定的技巧性,这里
我们主要介绍深度优先搜索(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;
}
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();
}
它从某个状态开始,不断地转移状态直到无法转移,然后回退到前一步的状态,继续转移到其他状态,如此不断重复,
直到找到最终的解。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
题目描述
给定整数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;
}
——————————————我是一个华丽丽的分割线————————————————
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
题目描述
有一个大小为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;
}
未完待续······