洛谷P1126-机器人搬重物-BFS/SPFA

洛谷P1126-机器人搬重物-BFS/SPFA

Description

机器人移动学会(RMI)现在正尝试用机器人搬运物品。机器人的形状是一个直径1.6米的球。在试验阶段,机器人被用于在一个储藏室中搬运货物。储藏室是一个N*M的网格,有些格子为不可移动的障碍。机器人的中心总是在格点上,当然,机器人必须在最短的时间内把物品搬运到指定的地方。机器人接受的指令有:向前移动1步(Creep);向前移动2步(Walk);向前移动3步(Run);向左转(Left);向右转(Right)。每个指令所需要的时间为1秒。请你计算一下机器人完成任务所需的最少时间。

Input

输入的第一行为两个正整数N,M(N,M<=50),下面N行是储藏室的构造,0表示无障碍,1表示有障碍,数字之间用一个空格隔开。接着一行有四个整数和一个大写字母,分别为起始点和目标点左上角网格的行与列,起始时的面对方向(东E,南S,西W,北N),数与数,数与字母之间均用一个空格隔开。终点的面向方向是任意的。

Output

一个整数,表示机器人完成任务所需的最少时间。如果无法到达,输出-1。

Sample Input

9 10
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0
7 2 2 7 S

Sample Output

12

Hint

img

思路

很明显可以用BFS来解决,不过今天是最短路专题,选这个就是因为其实很大一部分的BFS都可以转化为SPFA来解决,借用大佬的讲解说一下建图思路

很显然,这个题目是一个搜索,我们可以用i,j,k来唯一标识它的状态,i表示行,j表示列,k表示方向(自己定义即可),对于每一个状态,有5种转移方式(左转,右转,前1,前2,前3),而这5种转移是等价的(均耗费时间1),因此可以很好的用广度优先搜索来解决(广搜是基于路径相等的一种搜索,在这里它搜索的不再是点,而是加上方向共同表示的状态),由于边权均为1 ,所以先搜到的状态一定是时间最少的,它不用再入队列。而对于不等价的情况(转移时时间不都是1),或是有其他转换的更复杂的情况,搜索是无法解决的,这里主要讲一下图的构建。

一个状态可以向左转,向右转,进1,2,3,所以把每个状态与其可转移的状态连接,边权赋为所需时间,

每一个状态即为图中的点,为了处理简便,我们用一个单独的数代表i,j,k,这里可以用放缩,即定义

s=x*i+y*j+k,做到可以唯一标识每一个状态; 特别注意,这是一个有向图,你不能倒着走 时间复杂度:50*50*4 共10000个状态,每个点最多5条边,边数为50000,SPFA可以承受!

注意初始化,不能达到的点不再处理!

AC代码

#include <iostream>
#include <queue>
#include <iomanip>
#include <algorithm>
#include <cstring>
#include <string>
#define LL long long
#define ULL unsigned long long
#define mem(a,n) memset(a,n,sizeof(a))
#define fread freopen("in.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)
#define N 1010
#define INF 0x3f3f3f3f
#define eps 1e-9
using namespace std;
const int xx[4]={-1,0,1,0};
const int yy[4]={0,1,0,-1};
struct Con{
    int x,y,dir;
    Con(int a,int b,int c):x(a),y(b),dir(c){}
};
struct Edge{
    Con from,to;
    int dist;
    Edge(Con u,Con v,int d):from(u),to(v),dist(d){};
};
struct SPFA{
    vector<Edge> edges;
    vector<int> G[110][110][4];
    bool mp[110][110];
    bool inque[110][110][4];
    int d[110][110][4];
    int cnt[110][110][4];
    int n,m;

    bool valid(int x,int y){
        return x>0&&y>0&&x<n&&y<m&&!mp[x][y];
    }

    void init(int n,int m){
        this->n=n;
        this->m=m;
        int temp;
        mem(mp,0);
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                scanf("%d",&temp);
                if(temp){
                    mp[i][j]=mp[i+1][j]=mp[i][j+1]=mp[i+1][j+1]=true;
                }
            }
        }
        for(int i=0;i<55;++i){
            for(int j=0;j<55;++j){
                for(int k=0;k<4;++k){
                    G[i][j][k].clear();
                }
            }
        }
        bool canwalk; 
        edges.clear();
        for(int i=1;i<n;++i){
            for(int j=1;j<m;++j){
                if(!mp[i][j]){
                    for(int k=0;k<4;++k){
                        this->AddEdge(Con(i,j,k),Con(i,j,(k+1)%4),1);
                        this->AddEdge(Con(i,j,k),Con(i,j,(k+3)%4),1);
                        canwalk=true;
                        for(int len=1;canwalk&&len<4;++len){
                            if(valid(i+yy[k]*len,j+xx[k]*len)){
                                this->AddEdge(Con(i,j,k),Con(i+yy[k]*len,j+xx[k]*len,k),1);
                            }else{
                                canwalk=false;
                            }
                        }
                    }
                }
            }
        }
    }

    void AddEdge(Con from,Con to,int dist){
        edges.push_back(Edge(from,to,dist));
        m=edges.size();
        G[from.x][from.y][from.dir].push_back(m-1);
    }

    bool spfa(Con s){
        queue<Con> que;
        memset(inque,0,sizeof(inque));
        memset(cnt,0,sizeof(cnt));
        memset(d,INF,sizeof(d));
        que.push(s);
        d[s.x][s.y][s.dir]=0;
        while(!que.empty()){
            Con temp=que.front();
            if(cnt[temp.x][temp.y][temp.dir]==n){
                return true;
            }
            que.pop();
            inque[temp.x][temp.y][temp.dir]=false;
            for(int i=0;i<G[temp.x][temp.y][temp.dir].size();++i){
                Edge &e=edges[G[temp.x][temp.y][temp.dir][i]];
                if(d[e.to.x][e.to.y][e.to.dir]>d[temp.x][temp.y][temp.dir]+e.dist){
                    d[e.to.x][e.to.y][e.to.dir]=d[temp.x][temp.y][temp.dir]+e.dist;
                    if(!inque[e.to.x][e.to.y][e.to.dir]){
                        que.push(e.to);
                        inque[e.to.x][e.to.y][e.to.dir]=true;
                        if(++cnt[e.to.x][e.to.y][e.to.dir]>=n)  return true;
                    }
                }
            }
        }
        return false;
    }
};
SPFA SMF;
int main()
{
    int n,m,u,v,c,d,dir;
    char s;
    while(~scanf("%d%d",&n,&m)){
        SMF.init(n,m);
        scanf("%d%d%d%d%*c%c",&u,&v,&c,&d,&s);
        switch(s){
            case 'S':dir=1;
                break;
            case 'W':dir=0;
                break;
            case 'N':dir=3;
                break;
            case 'E':dir=2;
                break;
        }
        SMF.spfa(Con(u,v,dir));
        int ans=INF;
        for(int i=0;i<4;++i){
            ans=min(ans,SMF.d[c][d][i]);
        }
        printf("%d\n",(ans==INF?-1:ans));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值