图的遍历也称为搜索,就是从图中某个顶点出发,沿着一些边遍历图中所有的顶点,且每个顶点仅被访问一次,遍历可采取两种不同的方式:深度优先搜索(DFS)和广度优先搜索(BFS)。

1.DFS算法思想`

从顶点v出发深度遍历图G的算法

① 访问v0顶点,置vis[v0]=1,搜索v0未被访问的邻接点w,若存在邻接点w,则dfs(w),直到到达所有邻接点都被访问过的顶点u为止,接着退回一步,看是否还有其他没有被访问的邻接点。如果有,则访问此顶点,进行前述类似的访问,如果没有,就在退回一步进行搜索,重复上述过程直到所有顶点都被访问过。

② dfs算法最大特色就在于其递归特性,使得算法代码简洁。

2. BFS算法思想

从顶点v出发广度遍历图G的算法

①  访问v0顶点,置vis[v0]=1,依次访问顶点v0各个未被访问的邻接点,将v0各个未被访问的邻接点都访问到,分别从这些邻接点出发,依次访问他们未被访问的邻接点,直到所有顶点都被访问过。常用队列实现。

② 非递归,好理解。

基本思想讲完了,下面说说今天做的四道搜索题吧。

1.POJ2251 Dungeon Master:http://poj.org/problem?id=2251

题意:给出一三维空间的地牢,要求求出由字符'S'到字符'E'的最短路径

移动方向可以是上,下,左,右,前,后,六个方向

每移动一次就耗费一分钟,要求输出最快的走出时间。

不同L层的地图,相同RC坐标处是连通的

思路:这道题和之前做的走迷宫什么的类似,就是多了一维,做的时候把每一维的次序弄乱了,调了很久才搞清楚。

int dx[6]= {0,0,0,0,1,-1};
int dy[6]= {0,0,1,-1,0,0};
int dz[6]= {1,-1,0,0,0,0};
用三个数组分别存x方向,y方向,z方向的操作。我用的BFS,建图的时候建对,就没什么问题了。
附上代码:
 #include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
int visit[][][];
int step[][][];
int dx[]= {,,,,,-};
int dy[]= {,,,-,,};
int dz[]= {,-,,,,};
int n,m,k,flag;
struct node
{
int x,y,z;
} start,end;
int bfs()
{
flag=;
queue <node> Q;
Q.push(start);
node temp;
visit[start.x][start.y][start.z]=;
while(!Q.empty())
{
temp=Q.front();
Q.pop();
if(temp.x==end.x&&temp.y==end.y&&temp.z==end.z)
{
flag=;
break;
}
int x=temp.x;
int y=temp.y;
int z=temp.z;
for(int i=; i<; i++)
{
if(!visit[z+dz[i]][x+dx[i]][y+dy[i]]&&x+dx[i]<m&&x+dx[i]>=&&y+dy[i]<k&&y+dy[i]>=&&z+dz[i]<n&&z+dz[i]>=)
{
temp.x=x+dx[i];
temp.y=y+dy[i];
temp.z=z+dz[i];
visit[temp.z][temp.x][temp.y]=;
Q.push(temp);
step[temp.x][temp.y][temp.z]=step[x][y][z]+;
}
}
}
return flag;
}
int main()
{
int i,j,t;
char ch;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
if(n==&&m==&&k==)
break;
memset(step,,sizeof(step));
getchar();
for(i=; i<n; i++)
{
for(j=; j<m; j++)
{
for(t=; t<k; t++)
{
scanf("%c",&ch);
if(ch=='.')
visit[i][j][t]=;
else if(ch=='#')
visit[i][j][t]=;
else if(ch=='S')
{
visit[i][j][t]=;
start.x=j;
start.y=t;
start.z=i;
}
else if(ch=='E')
{
visit[i][j][t]=;
end.x=j;
end.y=t;
end.z=i;
}
}
getchar();
}
if(i!=n-)
getchar();
}
if(bfs())
printf("Escaped in %d minute(s).\n",step[end.x][end.y][end.z]);
else
printf("Trapped!\n");
}
return ;
}

2.POJ 1426 Find The Multiple  :http://poj.org/problem?id=1426

题意: 给出n,求由0或1组成的一个整数能整除n。

思路:我是暴力加bfs水过的,用C++交时间超限了,看到有人说用G++交不会超时,一交果然AC了。。不会优化,先附上丑陋代码:

 #include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
int n;
long long bfs()
{
queue<long long> Q;
while(!Q.empty())
{
Q.pop();
}
Q.push();
while()
{
long long sum=Q.front();
if(sum%n==)
return sum;
Q.pop();
Q.push(sum*);
Q.push(sum*+);
}
}
int main()
{
while(scanf("%d",&n)!=EOF&&n)
{
printf("%lld\n",bfs());
}
return ;

看到网上别人思路:

    解题思路:

首先暴力枚举肯定是不可能的 1000ms 想不超时都难,而且枚举还要解决大数问题。。

要不是人家把这题放到搜索,怎么也想不到用BFS。。。

    解题方法: BFS+同余模定理

不说废话

    首先说说朴素的不剪枝搜索方法:

我以n=6为例

首先十进制数,开头第一个数字(最高位)一定不能为0,即最高位必为1

设6的 ”01十进制倍数” 为k,那么必有k%6 = 0

现在就是要用BFS求k值

1、先搜索k的最高位,最高位必为1,则此时k=1,但1%6 =1  !=  0

因此k=1不是所求,存储余数 1

2、搜索下一位,下一位可能为0,即 k*10+0,此时k=10,那么k%6=4

可能为1,即 k*10+1,此时k=11,那么k%6=5

由于余数均不为0,即k=10与k=11均不是所求

3、继续搜索第三位,此时有四种可能了:

对于k=10,下一位可能为0,即 k*10+0,此时k=100,那么k%6=4

下一位可能为1,即 k*10+1,此时k=101,那么k%6=5

对于k=11,下一位可能为0,即 k*10+0,此时k=110,那么k%6=2

下一位可能为1,即 k*10+1,此时k=111,那么k%6=3

由于余数均不为0,即k=100,k=101,k=110,k=111均不是所求

4、继续搜索第四位,此时有八种可能了:

对于k=100,下一位可能为0,即 k*10+0,此时k=1000,那么k%6=4

下一位可能为1,即 k*10+1,此时k=1001,那么k%6=5

对于k=101,下一位可能为0,即 k*10+0,此时k=1010,那么k%6=2

下一位可能为1,即 k*10+1,此时k=1011,那么k%6=3

对于k=110,下一位可能为0,即 k*10+0,此时k=1100,那么k%6=2

下一位可能为1,即 k*10+1,此时k=1101,那么k%6=3

对于k=111,下一位可能为0,即 k*10+0,此时k=1110,那么k%6=0

下一位可能为1,即 k*10+1,此时k=1111,那么k%6=1

我们发现k=1110时,k%6=0,即1110就是所求的倍数

从上面的演绎不难发现,用BFS是搜索 当前位数字 (除最高位固定为1),因为每一位都只有0或1两种选择,换而言之是一个双入口BFS

本题难点在于搜索之后的处理:对余数的处理,对大数的处理,余数与所求倍数间的关系

接下来说说处理大数问题和剪枝的方法:

首先我们简单回顾一下 朴素搜索 法:

n=6

1%6=1  (k=1)

{

(1*10+0)%6=4  (k=10)

{

 (10*10+0)%6=4   (k=100)

{

  (100*10+0)%6=4  (k=1000)

        (100*10+1)%6=5  (k=1001)

}

    (10*10+1)%6=5  (k=101)

{

  (101*10+0)%6=2  (k=1010)

        (101*10+1)%6=3  (k=1011)

}

}

   (1*10+1)%6=5  (k=11)

{

    (11*10+0)%6=2   (k=110)

{

    (110*10+0)%6=2  (k=1100)

        (110*10+1)%6=3  (k=1101)

}

 (11*10+1)%6=3   (k=111)

{

        (111*10+0)%6=0  (k=1110)   有解

        (111*10+1)%6=1  (k=1111)  由于前面有解,这个余数不存储

}

}

}

从上面可以看出余数的存数顺序(逐层存储):

用数组mod[]存储余数,其中mod[0]不使用,由mod[1]开始

那么mod中的余数依次为: 1 4 5 4 5 2 3 4 5 2 3 2 3 0  共14个

即说明我们得到 余数0 之前,做了14步*10的操作,那么当n值足够大的时候,是很容易出现k为大数的情况(事实上我做过统计,200以内的n,有18个n对应的k值为大数

那么我们再用int去存储k就显得不怎么明智了。

为了处理所有情况,我们自然会想到 是不是应该要用int[]去存储k的每一位?

而又由于k是一个01序列,那能不能把 *10得到k每一位的问题 转化为模2的操作得到k的每一位(0或1) 呢?

答案是可以的

首先我们利用 同余模定理 对得到余数的方式进行一个优化

(a*b)%n = (a%n *b%n)%n

(a+b)%n = (a%n +b%n)%n

随便抽取上面一条式子为例

前一步 (11*10+1)%6=2   即k=110 , k%6=2

当前步 (110*10+1)%6=2

由同余模定理  (110*10+1)%6 = ((110*10)%6+1%6 )%6 = ((110%6 * 10%6)%6 +1 )%6

不难发现下划线部分110%6等于 (11*10+0)%6 = 2

所以当前步(110*10+1)%6可以转变为  (2*10+1)%6=2

很显然地,这种处理把k=110 等价于 k=2

即用 前一步操作得到的余数 代替 当前步的k值

而n在200的范围内, 余数值不可能超过3位数, 这就解决了 大数的问题

通过这种处理手法,我们只需在BFS时顺手存储一个 余数数组mod[] ,就能通过mod[i-1]得到mod[i]  ,直到mod[i]==0 时结束,大大减少了运算时间

前面已经提到,n=6时,求余操作进行了14次,对应地,BFS时*10的操作也进行了14次。

令i=14,通过观察发现,i%2恰好就是 6 的倍数的最低位数字

i/2  再令 i%2 ,恰好就是 6 的倍数的 次低位数字。。。

循环这个操作,直到i=0,就能得到 6的 01倍数(一个01队列),倒序输出就是所求

这样就完成了 *10操作到 %2操作的过渡

由于n值有限,只是1到200的整数,因此本题也可以用打表做,通过上面的方法得到结果后,就把1~200的倍数打印出来,重新建立一个程序,直接打表就可以了。

不过打表比上面介绍的方法快不了多少

代码:

 //Memory Time
//2236K 32MS #include<iostream>
using namespace std; int mod[]; //保存每次mod n的余数
//由于198的余数序列是最长的
//经过反复二分验证,436905是能存储198余数序列的最少空间
//但POJ肯定又越界测试了...524286是AC的最低下限,不然铁定RE int main(int i)
{
int n;
while(cin>>n)
{
if(!n)
break; mod[]=%n; //初始化,n倍数的最高位必是1 for(i=;mod[i-]!=;i++) //利用同余模定理,从前一步的余数mod[i/2]得到下一步的余数mod[i]
mod[i]=(mod[i/]*+i%)%n;
//mod[i/2]*10+i%2模拟了BFS的双入口搜索
//当i为偶数时,+0,即取当前位数字为0 。为奇数时,则+1,即取当前位数字为1 i--;
int pm=;
while(i)
{
mod[pm++]=i%; //把*10操作转化为%2操作,逆向求倍数的每一位数字
i/=;
}
while(pm)
cout<<mod[--pm]; //倒序输出
cout<<endl;
}
return ;
}

3.POJ 3087 Shuffle'm Up:http://poj.org/problem?id=3087

题意:

已知两堆牌s1和s2的初始状态, 其牌数均为c,按给定规则能将他们相互交叉组合成一堆牌s12,再将s12的最底下的c块牌归为s1,最顶的c块牌归为s2,依此循环下去。

现在输入s1和s2的初始状态 以及 预想的最终状态s12

问s1 s2经过多少次洗牌之后,最终能达到状态s12,若永远不可能相同,则输出"-1"。

思路:从题意来看,直接模拟就好了,用不到BFS。好吧,我也的确直接用模拟做了,

附上代码:

 #include<stdio.h>
#include<string.h>
int main()
{
int t,l,i,j;
char s1[],s2[],s[],s12[],s3[];
scanf("%d",&t);
for(j=; j<=t; j++)
{
int ans=;
scanf("%d",&l);
getchar();
gets(s1);
gets(s2);
gets(s12);
strcpy(s,s1);
strcpy(s+l,s2);
s3[]='\0';
while(strcmp(s3,s12)!=)
{
for(i=; i<l; i++)
{
s3[i*+]=s1[i];
s3[i*]=s2[i];
}
s3[*l]='\0';
ans++;
if(strcmp(s3,s)==)
{
ans=-;
break;
}
for(i=; i<l; i++)
s1[i]=s3[i];
strcpy(s2,s3+l);
}
printf("%d %d\n",j,ans);
}
return ;
}

4.POJ 2488 A Knight's Journey:http://poj.org/problem?id=2488

题意:

马从A1开始走“日”,如果能走遍棋盘上所有点,输出路径,否则,输出impossible.

思路:

定义了一个结构体变量,存该step时的位置。

int dir[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};可以保证最后按字典序输出。
做的时候还是没按字典序输出,最后发现是行和列搞反了。
附上代码:
 #include<stdio.h>
#include<string.h>
struct node
{
char c;
char z;
}move[];
int n,m;
int dir[][]={{-,-},{-,},{-,-},{-,},{,-},{,},{,-},{,}};
int visit[][];
int flag;
int dfs(int x,int y,int step)
{
int xx,yy,i;
if(step==n*m)
{
flag=;
return ;
}
for(i=; i<; i++)
{
xx=x+dir[i][];
yy=y+dir[i][];
if(xx>= && xx<=m&& yy>= && yy<=n&& !visit[xx][yy])
{
move[step].c=xx-+'A';
move[step].z=yy+'';
visit[xx][yy]=;
dfs(xx,yy,step+);
if(flag)
return ;
visit[xx][yy]=;
}
}
return ;
}
int main()
{
int t,i,k;
scanf("%d",&t);
for(k=; k<=t; k++)
{
flag=;
memset(visit,,sizeof(visit));
memset(move,,sizeof(move));
scanf("%d%d",&n,&m);
visit[][]=;
move[].c='A';
move[].z='';
printf("Scenario #%d:\n",k);
if(dfs(,,))
{
for(i=;i<n*m;i++)
{
printf("%c%c",move[i].c,move[i].z);
}
}
else
printf("impossible");
printf("\n\n");
}
return ;
}

总结完了。。弱菜要去看昨晚Bestcoder的题了,噶呜~

												

搜索:POJ2251&POJ1426&POJ3087&POJ2488的更多相关文章

  1. poj分类 很好很有层次感。

    初期: 一.基本算法:      (1)枚举. (poj1753,poj2965)      (2)贪心(poj1328,poj2109,poj2586)      (3)递归和分治法.      ( ...

  2. 【转】POJ题目分类推荐 (很好很有层次感)

    OJ上的一些水题(可用来练手和增加自信) (poj3299,poj2159,poj2739,poj1083,poj2262,poj1503,poj3006,poj2255,poj3094)初期: 一. ...

  3. 【转】ACM训练计划

    [转] POJ推荐50题以及ACM训练方案 -- : 转载自 wade_wang 最终编辑 000lzl POJ 推荐50题 第一类 动态规划(至少6题, 和 必做) 和 (可贪心) (稍难) 第二类 ...

  4. POJ 题目分类(转载)

    Log 2016-3-21 网上找的POJ分类,来源已经不清楚了.百度能百度到一大把.贴一份在博客上,鞭策自己刷题,不能偷懒!! 初期: 一.基本算法: (1)枚举. (poj1753,poj2965 ...

  5. (转)POJ题目分类

    初期:一.基本算法:     (1)枚举. (poj1753,poj2965)     (2)贪心(poj1328,poj2109,poj2586)     (3)递归和分治法.     (4)递推. ...

  6. acm常见算法及例题

    转自:http://blog.csdn.net/hengjie2009/article/details/7540135 acm常见算法及例题  初期:一.基本算法:     (1)枚举. (poj17 ...

  7. poj分类

    初期: 一.基本算法:      (1)枚举. (poj1753,poj2965)      (2)贪心(poj1328,poj2109,poj2586)      (3)递归和分治法.      ( ...

  8. 转载 ACM训练计划

    leetcode代码 利用堆栈:http://oj.leetcode.com/problems/evaluate-reverse-polish-notation/http://oj.leetcode. ...

  9. ACM算法总结及刷题参考

    参考:http://bbs.byr.cn/#!article/ACM_ICPC/11777 OJ上的一些水题(可用来练手和增加自信)(poj3299,poj2159,poj2739,poj1083,p ...

随机推荐

  1. 架构漫谈:自己开发一个Log框架

    前言 在日常开发中我们常常都会用到写日志的功能,现在网上的写Log的框架有很多,但是对于我个人而言,过于庞大:我们往往只为了使用框架中的某一个功能就不得不引用整个框架. 所以,我们今天就来自己动手开发 ...

  2. Qt调用DLL

    声明: 事先我已经自己动手写了一个简单的dll文件(myDLL.dll),C版接口的.并且用我前两篇有关DLL文章里面的方法,从dll中导出了导入库(.lib)文件,dll中有两个函数,原型如下:   ...

  3. Ruby on Rails: 使用devise+cancan+rolify建立完整的权限管理系

    devise.cancan和rolify这三个组件结合,可以建立完整而强大的用户权限模型. devise介绍,负责用户注册.登录.退出.找回密码等操作.细节参考devise on github can ...

  4. 高级特性(8)- JavaBean构件

    8.1 为何使用Bean8.2 编写Bean的过程8.3 使用Bean构造应用程序 8.3.1 将Bean打包成JAR文件 8.3.2 在开发环境中组合Bean8.4 Bean属性与事件的命名模式8. ...

  5. Route@书写规则的总结

    路由书写规则的总结 概念:Routing System由一组路由组成,每一个路由规则可以匹配一种类型的URL,在请求过来的时候,Ruting ystem 就用它来处理这个URL,路由的任务就是匹配UR ...

  6. premake Ubuntu下的安装

    premake是个跨平台的编译工具,先看看在Ubuntu下怎么安装. 首先下载,在/usr目录下: sudo wget -O premake-4.4-beta4-linux.tar.gz http:/ ...

  7. CCIE路由实验(10) -- IS-IS

    1.配置基本的集成IS-IS路由协议2.多区域的集成IS-IS配置3.IS-IS认证4.IS-IS路由泄露5.IS-IS汇总6.在IS-IS中手动产生一条默认路由7.IS-IS协议重分布8.用IS-I ...

  8. 基于visual Studio2013解决面试题之1002公共子串

     题目

  9. salon_百度百科

    salon_百度百科     salon    编辑    是法语Salon一字的译音,中文意即客厅,原指法国上层人物住宅中的豪华会客厅.从十七世纪,巴黎的名人(多半是名媛贵妇)常把客厅变成著名的社交 ...

  10. Swift - 同步请求获取网络数据

    使用NSURLConnection可以实现http通信.它提供了异步请求和同步请求两种通信方式. 注意:同步请求数据会造成主线程阻塞,必须请求结束后用户才能做其他的操作,所有通常在请求大数据或者网络不 ...