bfs搜索+剪枝

Olya and Energy Drinks
传送门:https://codeforces.com/problemset/problem/877/D
Olya loves energy drinks. She loves them so much that her room is full of empty cans from energy drinks.

Formally, her room can be represented as a field of n × m cells, each cell of which is empty or littered with cans.

Olya drank a lot of energy drink, so now she can run k meters per second. Each second she chooses one of the four directions (up, down, left or right) and runs from 1 to k meters in this direction. Of course, she can only run through empty cells.

Now Olya needs to get from cell (x1, y1) to cell (x2, y2). How many seconds will it take her if she moves optimally?

It’s guaranteed that cells (x1, y1) and (x2, y2) are empty. These cells can coincide.

Input
The first line contains three integers n, m and k (1 ≤ n, m, k ≤ 1000) — the sizes of the room and Olya’s speed.

Then n lines follow containing m characters each, the i-th of them contains on j-th position “#”, if the cell (i, j) is littered with cans, and “.” otherwise.

The last line contains four integers x1, y1, x2, y2 (1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m) — the coordinates of the first and the last cells.

Output
Print a single integer — the minimum time it will take Olya to get from (x1, y1) to (x2, y2).

If it’s impossible to get from (x1, y1) to (x2, y2), print -1.

Input
3 4 4
....
###.
....
1 1 3 1
Output
3
Input
3 4 1
....
###.
....
1 1 3 1
Output
8
Input
2 2 1
.#
#.
1 1 2 2
Output
-1

lya喜欢能量饮料。她非常爱他们,她的房间里充满了能量饮料的空罐头。

在形式上,她的房间可以表示为n×m个细胞的区域,每个细胞都是空的或散落着罐头。

Olya喝了很多能量饮料,所以现在她每秒可以跑k米。每一秒,她选择四个方向中的一个(上,下,左或右),并沿此方向从1到k米。当然,她只能穿过空单元格。

现在Olya需要从单元格(x1,y1)到单元格(x2,y2)。如果她最佳地移动,她会花多少秒?

保证单元格(x1,y1)和(x2,y2)为空。这些细胞可以重合。

每秒可以朝向一个方向走(1~k)步,问从起点走到终点最少需要多少秒。
bfs剪枝,该好好刷刷搜索专题了,剪枝就是先排除一些不可能的情况,使情况更加简单。
网上大佬用二进制枚举状态压缩,但是我不太会,可能运行慢一点,不过也能a

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
int n,m,k;
int a,b,c,d;
char maze[1005][1005];
bool vis[1005][1005];
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
struct node{
    int x,y,step;
}st,now;
bool check(int x,int y){
//判断它的下一步在不在房间里
    if(x<1||x>n||y<1||y>m)
        return false;
    return true;
}
int bfs(){
    if(a==c&&b==d){
    //特判自己走到自己
        return 0;
    }
    memset(vis,false,sizeof(vis));
    st.x=a;//把起点赋值给st
    st.y=b;
    st.step=0;
    queue<node>q;
    q.push(st);//放进队列
    vis[st.x][st.y]=true;//标记已走过
    while(!q.empty()){
        st=q.front();
        q.pop();
        int x,y;
        x=st.x;//这个点不能漏,漏了会wa
        y=st.y;
        now.step=st.step;
        for(int i=0;i<4;i++){
            for(int j=1;j<=k;j++){
                now.x=x+dir[i][0]*j;
                now.y=y+dir[i][1]*j;
                if(now.x==c&&now.y==d)
                    return now.step+1;
                if(!check(now.x,now.y)||maze[now.x][now.y]=='#')
                    break;//剪枝,如果不在房间里或者不能走,直接break,说明这个点走不到
                if(vis[now.x][now.y]==false){
                    vis[now.x][now.y]=true;
                    st.x=now.x;
                    st.y=now.y;
                    st.step=now.step+1;
                    q.push(st);
                }
            }
        }
    }
    return -1;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            cin>>maze[i][j];
    }
    scanf("%d%d%d%d",&a,&b,&c,&d);
    printf("%d\n",bfs());
    return 0;
}

//附上别人状态压缩的版本,快的一批
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
 
typedef long long ll;
const int maxn = 1005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
 
int n,m,k;
int sx,sy,ex,ey;
int vis[maxn][maxn];
char mp[maxn][maxn];
int dis[maxn][maxn];
 
struct node
{
    int x,y;
}now,nex;
 
bool inbound(node a)
{
    return a.x>=1&&a.x<=n&&a.y>=1&&a.y<=m;
}
 
void bfs()
{
    queue<node>q;
    now.x=sx;
    now.y=sy;
    q.push(now);
    vis[sx][sy]=(1<<4)-1;           //起点只经过一次就好
    while(q.size())
    {
        now=q.front();
        q.pop();
        if(now.x==ex&&now.y==ey) return;  //搜到终点
        for(int i=0;i<4;i++)              //枚举方向
        for(int j=1;j<=k;j++)             //枚举走的步数
        {
            nex.x=now.x+dir[i][0]*j;
            nex.y=now.y+dir[i][1]*j;
            if(!inbound(nex)||mp[nex.x][nex.y]=='#') break;    //该位置不合法
            if(vis[nex.x][nex.y]&(1<<i)) break;                //从这个方向已经走过该位置
            int flag=0;
            if(!vis[nex.x][nex.y]) flag=1;                     //搜到没有走过的点
            vis[nex.x][nex.y]|=(1<<i);                         //更新状态
            if(flag)
            {
                dis[nex.x][nex.y]=dis[now.x][now.y]+1;
                q.push(nex);
            }
        }
    }
}
 
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",mp[i]+1);
    }
    scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
    bfs();
    if(vis[ex][ey]) printf("%d\n",dis[ex][ey]);
    else   puts("-1");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值