BFS(双端队列+状态压缩) - 拯救大兵瑞恩 - HDU 4845

BFS(双端队列+状态压缩) - 拯救大兵瑞恩 - HDU 4845

题意:

n×m(1,1)(n,m)给定n×m的迷宫,起点在左上角(1,1),终点在右下角(n,m)。

()()两个点之间有三种连通方式:墙(不连通),门(需要钥匙才能通过),直接连通。

n,m,pnmp首行输入三个整数n,m,p,n和m表示迷宫的行和列,p是冗余条件。

kk(x1,y1),(x2,y2)c再输入一行整数k,表示门和墙的总数,接着输入k行数据,每行包括两个坐标(x_1,y_1),(x_2,y_2)和c,

c=0(x1,y1),(x2,y2)c=0时,表示(x_1,y_1),(x_2,y_2)之间有墙,即不连通。

c>0(x1,y1),(x2,y2)c()c>0时,表示(x_1,y_1),(x_2,y_2)之间有门,c为门的编号(与钥匙要对应)。

ss然后输入整数s,表示钥匙的数量,最后输入s行数据,

x,y,q,(x,y)q每行包括x,y,q,表示在(x,y)处有q号门的钥匙。

现要求从左上角到右下角的最短距离。

Sample Input

4 4 9
9
1 2 1 3 2
1 2 2 2 0
2 1 2 2 0
2 1 3 1 0
2 3 3 3 0
2 4 3 4 1
3 2 3 3 0
3 3 4 3 0
4 3 4 4 0
2
2 1 2
4 2 1

Sample Output

14

样例如下图:
在这里插入图片描述
数据范围:

1n,m,p10x1x2+y1y2=10cp1qp1k150Time limit:1000msMemory limit:32768kB1≤n,m,p≤10,|x_1−x_2|+|y_1−y_2|=1,0≤c≤p,1≤q≤p,1≤k≤150\\Time \ limit:1000 ms,Memory \ limit:32768 kB


分析:

bfs若没有钥匙限制,可以直接bfs得到最短路。

加上钥匙的限制,我们需要拆点,进行状态压缩。

1010由于钥匙的数量不超过10,故我们用一个长度为10的二进制数来表示当前位置有哪些编号的钥匙。

便(x,y)id为了方便,我们将二维坐标映射到一维坐标,每个点(x,y)给一个编号id。

dis[i][state]istate距离数组dis[i][state]表示从源点到点i且当前拥有的钥匙为state的最短距离。

idcurj记当前点的编号为id,状态为cur,与之相连通的点的编号为j,则分两种情况:

key[id]0 state=cur  key[id]①、当前位置有钥匙key[id]:最优解一定是拿起钥匙,花费为0,则状态变为\ state=cur\ |\ key[id],

dis[j][state]=dis[id][cur]\qquad即dis[j][state]=dis[id][cur]。

()②、向周围四个方向扩展(直接连通或者有门且有对应的钥匙):

w[i]>0cur1\qquad判断能否开门:当前位置的门的编号w[i]>0,表示有门,\\\qquad若状态cur的对应的位置上不为1,表示没钥匙,不连通。

idjdis[j]>dis[id]+1dis[j]=dis[id]+1j\qquad否则两点连通,判断通过id再到j是否更近:\\\qquad若dis[j]>dis[id]+1,则更新dis[j]=dis[id]+1,将j入队继续扩展。

建图:

本题建图也有难度。

n×m首先我们将n×m的矩阵中的所有点依次编号,映射到一维坐标上来。

接着我们根据读入的门和墙先建立一条无向边,并将该边加入到集合中去。

0然后我们遍历整个图,通过集合判断,将相邻之间没有边的两点之间建立一条边,权值置为0,表示连通。

求解最短路:

0()1()广线由于边权仅存在0(拿钥匙)和1(移动位置)两种情况,故我们可以用双端队列广搜来解决,时间复杂度是线性的。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<deque>
#include<set>

#define P pair<int,int>
#define x first
#define y second

using namespace std;

const int N=11, M=N*N, E=N*(N-1)*2*2, S=1<<10;

int n,m,p,k;
int h[M],e[E],ne[E],w[E],idx;
int g[N][N];
int dis[M][S],key[M];
bool st[M][S];
set<P> edge;

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

void build()
{
    int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int u=0;u<4;u++)
            {
                int x=i+dir[u][0], y=j+dir[u][1];
                if(x<1||x>n||y<1||y>m) continue;
                int a=g[i][j],b=g[x][y];
                if(!edge.count({a,b})) add(a,b,0);   //不存在时再建边
            }
}

int bfs()
{
    memset(dis,0x3f,sizeof dis);
    dis[1][0]=0;
    
    deque<P> dq;
    dq.push_front({1,0});   //first:编号,second:状态
    
    while(dq.size())
    {
        P t=dq.front();
        dq.pop_front();

        if(t.x==n*m) return dis[t.x][t.y];
        
        int id=t.x,cur=t.y;
        if(st[id][cur]) continue;
        st[id][cur]=true;
        
        if(key[id])   //当前位置有钥匙,拿起来
        {
            int state=cur|key[id];
            dis[id][state]=dis[id][cur];
            dq.push_front({id,state});
        }
            
        for(int i=h[id];~i;i=ne[i])   //向相邻方向扩展
        {
            int j=e[i];    
            if(w[i] && !( cur>>w[i]-1 & 1)) continue;  //有门但没钥匙
            if(dis[j][cur]>dis[id][cur]+1)
            {
                dis[j][cur]=dis[id][cur]+1;
                dq.push_back({j,cur});
            }
        }
    }
    return -1;
}

int main()
{
    cin>>n>>m>>p>>k;
    
    for(int i=1,cnt=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            g[i][j]=cnt++;
    
    memset(h,-1,sizeof h);
    while(k--)
    {
        int x1,y1,x2,y2,t;
        cin>>x1>>y1>>x2>>y2>>t;
        int a=g[x1][y1],b=g[x2][y2];
        edge.insert({a,b}),edge.insert({b,a});
        if(t) add(a,b,t),add(b,a,t);
    }
    
    int kcnt;
    cin>>kcnt;
    while(kcnt--)
    {
        int x,y,t;
        cin>>x>>y>>t;
        key[g[x][y]] |= 1<<(t-1);   //映射为一个二进制数
    }
    
    build();
    
    cout<<bfs()<<endl;
    
    return 0;
}
展开阅读全文
©️2020 CSDN 皮肤主题: 1024 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值