Codeforces Round #442(Div.2)Problem D【思维优化bfs】

Olya and Energy Drinks
题目链接:http://codeforces.com/problemset/problem/877/D
题目大意:给你一张n*m的地图,人物Olya一秒可以走1~k步,问从起点到终点的最短时间
(1 ≤ \leq n,m,k ≤ \leq 1000)
Input

3 4 4
....
###.
....
1 1 3 1

Output

3

Note
In the first sample Olya should run 3 meters to the right in the first second, 2 meters down in the second second and 3 meters to the left in the third second.

用bfs是肯定的,bfs能到达的所有的地方,在普通的模板bfs中加一个循环表示步数就行了,但是,如何不重复搜是个问题
普通剪枝: 用二维vs数组标记已经访问过的点,已经访问过的点就不用再次访问了(因为这个题只是简单地计算步数时间,根据bfs宽搜的特点可以保证-----再次访问这个点时用的时间不会比第一次更短,所以是否也不用定义一个数组,判断如果到达该点的时间大于最短时间,就不用入队?但是很多博客都记录了最短时间,而且好像也可以不用优先队列,疑惑。。
神操作: 我们仍需要用vs数组来标记,但是之前的是标记该位置是否经过。现在需要保存该位置是否已遍历过这四个方向的情况,如果在这个方向已经遍历过了就直接break,这里我们用二进制来表示这个状态。
解释:假设现在只是用第一种方法标记,如下,假设现在从(1,1)点开始向右遍历,k=7,它遍历到第7个点时,1~7个点都已入队,且被标记了,然后继续看,它遍历到点2的时候,它又会向右遍历完所有的点(虽然右边的点经判断后不会入队),这样就会多了很多不必要的操作

 1 2 3 4 5 6 7 
1. . . . . . . 

代码:
在这里插入图片描述

#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=2e4+10;
const int INF=0x3f3f3f3f;
char mp[2000][2000];
int vs[2000][2000],n,m,k,x,y,sx,sy;
int dx[]= {0,0,-1,1};
int dy[]= {1,-1,0,0};
struct node
{
    int x,y,step;
} now,net;
int check(int x,int y)
{
    if(x<1||x>n||y<1||y>m)
        return 0;
    if(mp[x][y]=='#')
        return 0;
    return 1;
}
int bfs()
{
    queue<node>qu;
    now.x=x;
    now.y=y;
    now.step=0;
    vs[x][y]=1;
    qu.push(now);
    while(!qu.empty())
    {
        now=qu.front();
        qu.pop();
        if(now.x==sx&&now.y==sy)
            return now.step;
        for(int i=0; i<4; i++)
        {
            for(int j=1; j<=k; j++)
            {
                net.x=now.x+j*dx[i];
                net.y=now.y+j*dy[i];
                net.step=now.step+1;
                if(!check(net.x,net.y))break;//该点越界或为‘#’
                if(vs[net.x][net.y]&(1<<i))break;//主角,,该反方向上已经遍历过
                if(!vs[net.x][net.y])
                {
                    if(net.x==sx&&net.y==sy)
                        return net.step;
                    qu.push(net);
                }
                vs[net.x][net.y]|=(1<<i);
            }
        }
    }
    return -1;
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        memset(vs,0,sizeof(vs));
        for(int i=1; i<=n; i++)
            scanf("%s",mp[i]+1);
        scanf("%d%d%d%d",&x,&y,&sx,&sy);
        int ans=bfs();
        printf("%d\n",ans);
    }
}

后面用神操作a了之后,发现就用第一种方法也能过,不过有两个要注意的地方
1、Olya and Energy Drinks【Codeforces 877D】【BFS+思维+剪枝】
在这里插入图片描述
2、上面的问题解决了之后,在进入队列之前加了一个是否到达终点返回的条件就过了,不知道是不是巧合,不过以后写bfs都可以加上吧
在这里插入图片描述

#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=2e4+10;
const int INF=0x3f3f3f3f;
char mp[2000][2000];
int vs[2000][2000],n,m,k,x,y,sx,sy;
int dx[]= {0,0,-1,1};
int dy[]= {1,-1,0,0};
struct node
{
    int x,y,step;
} now,net;
int check(int x,int y)
{
    if(x<1||x>n||y<1||y>m)
        return 0;
    if(mp[x][y]=='#')
        return 0;
    return 1;
}
int bfs()
{
    queue<node>qu;
    now.x=x;
    now.y=y;
    now.step=0;
    vs[x][y]=1;
    qu.push(now);
    while(!qu.empty())
    {
        now=qu.front();
        qu.pop();
        if(now.x==sx&&now.y==sy)
            return now.step;
        for(int i=0; i<4; i++)
        {
            for(int j=1; j<=k; j++)
            {
                net.x=now.x+j*dx[i];
                net.y=now.y+j*dy[i];
                net.step=now.step+1;
                if(vs[net.x][net.y])
                    continue;
                if(check(net.x,net.y))
                {
                    if(net.x==sx&&net.y==sy)
                        return net.step;
                    qu.push(net);
                    vs[net.x][net.y]=1;
                }
                else
                    break;
            }
        }
    }
    return -1;
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        memset(vs,0,sizeof(vs));
        for(int i=1; i<=n; i++)
            scanf("%s",mp[i]+1);
        scanf("%d%d%d%d",&x,&y,&sx,&sy);
        int ans=bfs();
        printf("%d\n",ans);
    }
}

还有一道优先队列优化bfs的题:hdu 1242:Rescue
(因为广搜的过程中不能保证时间少的都在队头,所以应该放入优先队列,同时设置结构体时间少的为优先级高的)
代码:

#include <iostream>
#include <string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
typedef long long LL;
using namespace std;
const int manx=2e4+10;
const int INF=0x3f3f3f3f;
int n,m,vs[300][300],x,y,sx,sy;
int dx[]= {1,-1,0,0};
int dy[]= {0,0,-1,1};
char mp[300][300];
struct node
{
    int x,y,step;
    friend bool operator<(node a,node b)
    {
        return a.step>b.step;
    }
} now,net;
int check(int x,int y)
{
    if(x<1||x>n||y<1||y>m)
        return 0;
    if(vs[x][y])
        return 0;
    if(mp[x][y]=='#')
        return 0;
    return 1;
}
int bfs()
{
    priority_queue<node>qu;
    now.x=x;
    now.y=y;
    now.step=0;
    vs[x][y]=1;
    qu.push(now);
    while(!qu.empty())
    {
        now=qu.top();
        qu.pop();
        if(now.x==sx&&now.y==sy)
            return now.step;
        for(int i=0; i<4; i++)
        {
            net.x=now.x+dx[i];
            net.y=now.y+dy[i];
            if(check(net.x,net.y))
            {
                net.step=now.step+1;
                if(mp[net.x][net.y]=='x')
                    net.step++;
                qu.push(net);
                vs[net.x][net.y]=1;
            }
        }
    }
    return -1;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(vs,0,sizeof(vs));
        for(int i=1; i<=n; i++)
            scanf("%s",mp[i]+1);
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                if(mp[i][j]=='a')
                    sx=i,sy=j;
                else if(mp[i][j]=='r')
                    x=i,y=j;
        int ans=bfs();
        if(ans==-1)
            printf("Poor ANGEL has to stay in the prison all his life.\n");
        else
            printf("%d\n",ans);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值