拯救大兵瑞恩——分层图、拆点

题目描述

1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。

瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。

迷宫的外形是一个长方形,其南北方向被划分为 N 行,东西方向被划分为 M 列, 于是整个迷宫被划分为 N×M 个单元。

每一个单元的位置可用一个有序数对 (单元的行号, 单元的列号) 来表示。

南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。

注意: 门可以从两个方向穿过,即可以看成一条无向边。

迷宫中有一些单元存放着钥匙,同一个单元可能存放 多把钥匙,并且所有的门被分成 P 类,打开同一类的门的钥匙相同,不同类门的钥匙不同。

大兵瑞恩被关押在迷宫的东南角,即 (N,M) 单元里,并已经昏迷。

迷宫只有一个入口,在西北角。

也就是说,麦克可以直接进入 (1,1) 单元。

另外,麦克从一个单元移动到另一个相邻单元的时间为 1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。

试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。

输入格式
第一行有三个整数,分别表示 N,M,P 的值。

第二行是一个整数 k,表示迷宫中门和墙的总数。

接下来 k 行,每行包含五个整数,Xi1,Yi1,Xi2,Yi2,Gi:当 Gi≥1 时,表示 (Xi1,Yi1) 单元与 (Xi2,Yi2) 单元之间有一扇第 Gi 类的门,当 Gi=0 时,表示 (Xi1,Yi1) 单元与 (Xi2,Yi2) 单元之间有一面不可逾越的墙。

接下来一行,包含一个整数 S,表示迷宫中存放的钥匙的总数。

接下来 S 行,每行包含三个整数 Xi1,Yi1,Qi,表示 (Xi1,Yi1) 单元里存在一个能开启第 Qi 类门的钥匙。

输出格式
输出麦克营救到大兵瑞恩的最短时间。

如果问题无解,则输出 -1。

数据范围
|Xi1−Xi2|+|Yi1−Yi2|=1,
0≤Gi≤P,
1≤Qi≤P,
1≤N,M,P≤10,
1≤k≤150

输入样例:
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

输出样例:
14

解题思路

这是一道迷宫类型的最短路问题,相比之前遇到的迷宫类型的最短路问题多了一些条件(墙、门和钥匙),之前那些问题都是采用DP的方式直接进行求解,所以这道题也就想到了是否能够采用DP的方式来进行求解呢。很显然,直接采用之前二维坐标状态表示的方式来进行求解是行不通的,因为这道题中不只有二维坐标,还有钥匙的限制条件(墙和门属于地图的状态)。DP问题中,当状态还无法表示时,通常都是采用“加维”的方式,在这道题里,我们可以通过多加一维状态(在图论中,这种方式也就被叫做拆点了),表示所拥有钥匙的状态,因为钥匙的种类数顶多为10种,并且每把钥匙可以使用多次,所以只需要能够表示出拥有哪些钥匙就可以了,因此采用二进制的方式来表示所拥有钥匙的状态,即将状态表示为:DP[i][j][k],代表处于位置i、j,并且所拥有的钥匙状态为k(拥有第几类钥匙对应第i - 1位就为1,反之为0)
为了方便,将坐标的状态表示压缩为一维,即将坐标按从左到右再从上到下依次编号为1~N。
接下来考虑状态转移方程,每个位置的状态都可以从它上下左右四个状态转移而来,并且可以发现,从某一个位置走出,之后是完全有可能会重新回到该位置的,也就是该图是不满足拓扑序的,因此无法采用DP的方式来进行求解,但是我们可以采用图论中最短路问题的方式来进行求解,将相邻两个位置之间的道路状态(墙、门和通路)抽象为边,墙表示两个位置之间没有边,通路表示两个位置之间的道路权值为0,门的权值为相应钥匙代号。通过上述的方式,就可以将该迷宫地图抽象为一个平时图论中最短路问题的图了,而dp的状态表示就抽象为图中的每一个节点,然后再在这张图上进行求解。
通过分析可以发现,抽象之后的图的节点关系只有两种:
  (1)所处位置有钥匙,那么拿起所有的钥匙(很显然,拿起所有的钥匙一点都不亏,因为拿钥匙不花费时间,如果只拿其中的一些,那么有可能之后还需要返回来重新拿起之前没有拿的那一部分),状态转化为拿起钥匙后的状态,这种状态转移的时间消耗为0;
  (2)从所处位置走到下一个位置(上下左右),状态转化为下一个位置的状态,这种状态转移的时间消耗为1
在权值只有0和1的图上做最短路问题,那么可以直接采用双端队列的方式进行求解。

思路来源:AcWing 算法提高课

代码

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

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 11, M = 4 * N * N, P = 10;

int n, m, p;
int h[N * N], e[M], w[M], ne[M], idx;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值