您的位置:首页 > 其它

ACM暑假集训第一章——搜索

2012-07-31 08:32 337 查看
我要大二了,这个暑假,我开始我的ACM暑假集训,第一步学搜索。

搜索回顾

所学习的搜索的方法包括BFS(广搜)和DFS(深搜),通过练习,理解对迷宫类题目和隐式图的搜索。另外,对树结构的遍历,与邻接表的使用。

接触STL中的模版:<queue>里的 priority_queue和queue,<stack>里的stack。

1、迷宫类的题目:

/************************************************************************************************/

int dir[][]={}; //方向数组,二维用[][2];三维用[][3];

bool mark[MAXN][MAXN]; //标记数组,此为二维空间的标记

int tim[MAXN][MAXN], //这是记录时间的数组,可用于求到每个点的最小值

char Graph[MAXN][MAXN]; //字符记录图形

//*********dfs*********//

int pre[MAXN][MAXN][2]; //用深搜求最优解时,用此数组记录前一个位置,此为记录前(x, y)一个坐标

struct pos{

int ;

}stack[MAXN],top; //用栈存储轨迹,可应用于广搜(BFS)与深搜(DFS)

//bfs

struct pos{

int ;

}queue[MAXN*MAXN],S,T; //用队列存储轨迹,常用于广搜(BFS)

/***********************************************************************************************/

2、隐式图的搜索:(例题:hd1495非常可乐)

/*********************************************************************************************/

bool vis[MAXN][MAXN][MAXN]; //用三维数组记录各种状态

int x,y,z; //记录三个容器的容积

struct state{

int x,y,z,step;

}queue[MAXN*MAXN*MAXN],S; //组建状态的队列

bool ok(int s,int n,int m) //判断是否达到要求

{

if(s==n&&m==0||s==m&&n==0||n==m&&s==0)return true;

return false;

}

int BFS()

{

int front,rear,i;

state s,t;

memset(vis,false,sizeof(vis)); //初始化,标记所有状态都未被访问

front=rear=0;

queue[rear++]=S; //起始状态入队

vis[S.x][S.y][S.z]=true; //标记初始的状态

while(front<rear){ //队列结束条件,对头碰到队尾

s=queue[front++]; //出队

if(ok(s.x,s.y,s.z))return s.step;

if(s.x>0){ //从s.x倒出

t.x=s.x-(y-s.y); //从x倒到y

t.y=s.y+(y-s.y);

t.z=s.z+0;

t.step=s.step+1;

if(t.x>=0&&t.y>=0&&t.z>=0&&t.x<=x&&t.y<=y&&t.z<=z&&!vis[t.x][t.y][t.z]){

queue[rear++]=t; //将符合条件的状态入队

vis[t.x][t.y][t.z]=true; //标记入队的状态

}

t.x=s.x-(z-s.z); //从x倒到z

t.y=s.y+0;

t.z=s.z+(z-s.z);

t.step=s.step+1;

if(t.x>=0&&t.y>=0&&t.z>=0&&t.x<=x&&t.y<=y&&t.z<=z&&!vis[t.x][t.y][t.z]){

queue[rear++]=t;

vis[t.x][t.y][t.z]=true;

}

t.x=s.x-s.x; //从x倒到y

t.y=s.y+s.x;

t.z=s.z+0;

t.step=s.step+1;

if(t.x>=0&&t.y>=0&&t.z>=0&&t.x<=x&&t.y<=y&&t.z<=z&&!vis[t.x][t.y][t.z]){

queue[rear++]=t;

vis[t.x][t.y][t.z]=true;

}

t.x=s.x-s.x; //从x倒到z

t.y=s.y+0;

t.z=s.z+s.x;

t.step=s.step+1;

if(t.x>=0&&t.y>=0&&t.z>=0&&t.x<=x&&t.y<=y&&t.z<=z&&!vis[t.x][t.y][t.z]){

queue[rear++]=t;

vis[t.x][t.y][t.z]=true;

}

}

if(s.y>0){ //从s.y倒出

//类似s.x倒出,省略

}

if(s.z>0){ //从s.z倒出

//同上,省略

}

}

return 0;

}

int main()

{

while(scanf("%d%d%d",&x,&y,&z)!=EOF){

if(!x&&!y&&!z)return 0;

S.x=x;S.y=0;S.z=0;S.step=0; //起点的初始化

if(x%2==1){ //剪枝:起始值须为偶数

printf("NO\n");

continue;

}

int cnt=BFS(); //深搜求最少次数

if(cnt>0)printf("%d\n",cnt);

else printf("NO\n");

}

return 0;

}

总结:

从题目中寻找出隐含的搜索。

/************************************************************************************************/

3、对树结构的遍历(以二叉树为例)

/************************************************************************************************/

构建与插入:

struct Node{ //定义节点

int data; //节点上的数据

Node *left,*right; //节点的左儿子,右女儿

};

void travel(Node *node,int key) // 先序遍历

{

if(key==1)

printf("%d",node->data);

else

printf(" %d",node->data);

if(node->left!=NULL)

travel(node->left,2);

if(node->right!=NULL)

travel(node->right,2);

}

Node* insert(Node *node,int dig) // 插入节点

{

if(node==NULL){

node=(Node*)malloc(sizeof(Node));

node->data=dig;

node->left=node->right=NULL; //每次插入,都将该节点的左儿子,右女儿置空

return node;

}

if(dig<node->data)

node->left=insert(node->left,dig); //找空的左儿子

else

node->right=insert(node->right,dig); //找空的右女儿

return node;

}

Node *node=NULL; //二叉树的根节点

node=insert(node,dig); //插入dig到相应的节点

/******************************************************************************************/

Node* insert(Node *p,char *preord,char *inord,int len) //已知DLR遍历与LDR遍历推出LRD遍历

{

int i;

if(p==NULL){

p=(Node*)malloc(sizeof(Node));

p->left=NULL; p->right=NULL;

}

for(i=0;i<len;i++)

if(inord[i]==preord[0]){

p->data=preord[0];

p->left=NULL;

p->right=NULL;

if(i>0)p->left=insert(p->left,preord+1,inord,i);

if(len-i-1>0)p->right=insert(p->right,preord+i+1,inord+1+i,len-i-1);

return p;

}

}

void travel(Node *p) //LRD遍历

{

if(p->left!=NULL)travel(p->left);

if(p->right!=NULL)travel(p->right);

printf("%c",p->data);

return ;

}

/************************************************************************************************/

总结:

了解二叉树的存储结构,但要去了解二叉树如何用数组存储。

4、邻接表(定义、构建、遍历<使用结构体>)

/************************************************************************************************/

struct Edge{

int to,w,next; //分别为每条边上的出点、权重、前一条边

}list[MAXN]; //定义邻接表

int tot,node[MAXN]; //分别为边数(初始值定为0),每个点的边数下标值,初始值赋-1

inline void add_edge(int s,int e,int w) //加边处理,分别为起点,出点,权重

{

list[tot].to=e;

list[tot].w=w;

list[tot].next=node[s];

node[s]=tot++;

}

add__edge(s,e,w); //此为加s->e的边,如要双向边,加一条add_edge(e,s,w)语句即可

for(i=node[s];i!=-1;=list[i].next) //此循环可找出与s点相接的所有边,先找到后加入的,再找到先加入的

list[i].w; list[i].to; //依次为该边的权重和出点

/************************************************************************************************/

总结:

使用邻接表来存储路径很合适,调用快,不会取到无边的两点,可应用于求最短路。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: