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; //依次为该边的权重和出点
/************************************************************************************************/
总结:
使用邻接表来存储路径很合适,调用快,不会取到无边的两点,可应用于求最短路。
搜索回顾
所学习的搜索的方法包括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; //依次为该边的权重和出点
/************************************************************************************************/
总结:
使用邻接表来存储路径很合适,调用快,不会取到无边的两点,可应用于求最短路。
相关文章推荐
- ACM暑假集训之最小生成树
- 2015暑假ACM集训结训赛(第一场)(SDUTOJ)
- 暑假集训日记--8.3--搜索
- 暑假集训--训练1 二分搜索
- CSU-ACM2014暑假集训基础组训练赛(1) 解题报告
- 【暑假集训】之被ACM金牌大神虐的第二天之图论篇
- ACM暑假集训日记 17.8.2
- ACM暑假集训日记 17.8.5
- XYNU—ACM暑假集训第四次测试 STL
- 暑假集训日记--8.8--搜索+练习赛
- ACM暑假集训日记 17.8.14
- ACM暑假集训日记 17.8.16 树状数组
- 暑假集训-搜索
- ACM 学习心得 ——2014年ACM暑假集训有感
- POJ 1330 大一暑假ACM集训
- XYNU—ACM暑假集训第三次测试 贪心算法
- CSU-ACM2016暑假集训训练1-二分搜索-C
- ACM暑假集训日记 17.8.10
- ACM暑假集训(0)
- 暑假集训第三周第二阶段 搜索 B - 免费馅饼