搜索的前提是,我们始终相信总能搜索到结果。搜索不到也是搜索的一种结果
。搜索主要方法主要有两种,枚举法和回溯法。枚举,我们事先知道结果或约束条件范围,典型例子就是暴力穷举。优点是,简单容易理解,但是,往往效率就比较低。
回溯法经常用于,我们不知道求解过程中会走到哪一步,但是目的明确,步与步之间有一定的相似关系。经常是用多重循环来实现。
由于直译枚举或者暴力穷举,是考察大量状态,穷举所有状态。所以是思考量最小的,最简单易懂的。经常是用多重循环来实现。往往通过降维来提升效率,降低时间复杂度。这里来举个例子。
•
SubRaY有一天得到一块西瓜,是长方体形的…(我的世界吗)
[题目描述]SubRaY发现这块西瓜长m厘米,宽n厘米,高h厘米.他发现如果把这块西瓜平均地分成mnh块1立方厘米的小正方体,那么每一小块都会有一个营养值(可能为负,因为西瓜是有可能坏掉的,但是绝对值不超过200).
现在SubRaY决定从这mnh立方厘米的西瓜中切出mmnnhh立方厘米的一块小西瓜(一定是立方体形,长宽高均为整数),然后吃掉它.他想知道他最多能获得多少营养值.(0<=mm<=m,0<=nn<=n,0<=hh<=h.mm,nn,hh的值由您来决定).
换句话说,我们希望从一个mnh的三维矩阵中,找出一个三维子矩阵,这个子矩阵的权和最大.
•
Example Input
•
2 3 4
•
4 1 2 8
•
0 5 -48 4
•
3 0 1 9
•
2 1 4 9
•
1 0 1 7
•
3 1 2 8
•
Example Output
•
45
看到这道题,第一个想法是枚举所有大小的西瓜,每一种切法切出来的西瓜,在计算它的营养值进行比较。
但是想一想,不就是个求和吗。计算从点1,1,1到点x,y,z的累加和并记录在sum[i,j,k]。然后枚举西瓜的左上角和右下角的点。Result=Sum[i,j,k]-……这个我不想算了,自己拿着演草纸画画算出来那个公式都行了。。。
二分枚举的前提是,数列是具有单调性的。穷举之前先排个序,甚至本身就是有序。通常是求满足条件的最佳值。可以用while循环来写也可以用递归来写。
left := 1; right := n;
while (left<right) do
{
mid := (left+right)/2;
if (条件) left := mid + 1;
else right := mid;
}
因为二分是折半之后再折半,所以二分的时间复杂度是 log2(n)。这是一个很强大的效率:
n=1024, log2(n)=10;
n=1024*1024 log2(n)=20;
所以,数据量越大,二分的优越性越明显。
回溯法是一种能避免不必要搜索的穷举式的搜索算法。通常是用多重循环来实现。例如经典的八皇后问题。
Floodfill算法
Floodfill种子染色法也属于回溯发。它就像一滴墨水滴进自来水一样慢慢扩散出去或感染它周围的区域。floodfill可以用递归来写,也可以用队列来写,相对来说,用借助于队列来写会更稳定一点。这里举个例子:
Cell
•
一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如阵列:
输入:整数m,n(m行,n列 (0<m,n<=100))矩阵
输出:细胞的个数。
样例:
输入:
4 10
0234500067
1034560500
2045600671
0000000089
输出:
4
这道题就是找有几片非0的区域
Int dx[1…4]=(-1,0,1,0);//dx,dy是当前点坐标x,y可以变化的量
Int dy[1…4] =(0,1,0,-1);
用1到4的循环来上下左右找
For(int i:=0;i<4 ;i++)
{
xx:=x+dx[i];
yy:=y+dy[i];
}
从这一点出发将周围非0的点感染为0。代码如下:
#include <stdio.h>
#include <stdlib.h>
int
n,m,cx[4]={0,1,0,-1},cy[4]={1,0,-1,0};
char c[1010][1010];
void ff(int x,int y)
{
int j=0;
c[x][y]='0';
for(;j<4;j++)
if(c[x+cx[j]][y+cy[j]]!='0'&&x+cx[j]>0&&x+cx[j]<=m&&y+cy[j]>0&&y+cy[j]<=n)
ff(x+cx[j],y+cy[j]);
}
int main()
{
int sum=0,i,j;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)scanf(" %c",&c[i][j]);/*c而当前的scanf是在接收字符
(即用%c控制输入)时。由于前面的输入语句(不一定是scanf)把最后输入的'\n'遗留在了输入缓冲区就是它把回车当输入了*/
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
if(c[i][j]!='0'){sum++;ff(i,j);}
printf("有%d个细胞",sum);
scanf("");//防止输出一闪而过
return 0;
}
老师教的变成c了。。。。我也转了 不过vc++用起来好难受啊。。。没mingw和vs好用竟然还有bug。。。。差评