4105:拯救公主
-
总时间限制:
- 1000ms 内存限制:
- 65536kB
-
描述
-
多灾多难的公主又被大魔王抓走啦!国王派遣了第一勇士阿福去拯救她。
身为超级厉害的术士,同时也是阿福的好伙伴,你决定祝他一臂之力。你为阿福提供了一张大魔王根据地的地图,上面标记了阿福和公主所在的位置,以及一些不能够踏入的禁区。你还贴心地为阿福制造了一些传送门,通过一个传送门可以瞬间转移到任意一个传送门,当然阿福也可以选择不通过传送门瞬移。传送门的位置也被标记在了地图上。此外,你还查探到公主所在的地方被设下了结界,需要集齐K种宝石才能打开。当然,你在地图上也标记出了不同宝石所在的位置。
你希望阿福能够带着公主早日凯旋。于是在阿福出发之前,你还需要为阿福计算出他最快救出公主的时间。
地图用一个R×C的字符矩阵来表示。字符S表示阿福所在的位置,字符E表示公主所在的位置,字符#表示不能踏入的禁区,字符$表示传送门,字符.表示该位置安全,数字字符0至4表示了宝石的类型。阿福每次可以从当前的位置走到他上下左右四个方向上的任意一个位置,但不能走出地图边界。阿福每走一步需要花费1个单位时间,从一个传送门到达另一个传送门不需要花费时间。当阿福走到宝石所在的位置时,就视为得到了该宝石,不需要花费额外时间。
输入
-
第一行是一个正整数T(1 <= T <= 10),表示一共有T组数据。
每一组数据的第一行包含了三个用空格分开的正整数R、C(2 <= R, C <= 200)和K,表示地图是一个R×C的矩阵,而阿福需要集齐K种宝石才能够打开拘禁公主的结界。
接下来的R行描述了地图的具体内容,每一行包含了C个字符。字符含义如题目描述中所述。保证有且仅有一个S和E。$的数量不超过10个。宝石的类型在数字0至4范围内,即不会超过5种宝石。
输出
- 对于每一组数据,输出阿福救出公主所花费的最少单位时间。若阿福无法救出公主,则输出“oop!”(只输出引号里面的内容,不输出引号)。每组数据的输出结果占一行。
样例输入
1 7 8 2 ........ ..S..#0. .##..1.. .0#..... ...1#... ...##E.. ...1....
-
样例输出
-
11
明显的广度优先搜索,但是好像条件多了许多,首先需要有什么东西记录当前获得的宝石类型,看到网上的大神们说了用到状态压缩,将5种拥有宝石情况压缩,11111表示五种宝石都有,11000表示只有3,4两种类型宝石,所以开辟数组的时候多开了好多数组,然而用到的就没那么多了,五种宝石,那么三维需要开32个,用到的只有1,3,7,15,31而已。
这是一个状态压缩问题,尽量把自己的思路写详细一点。最主要的部分就是用一个三维数组dp[tx][ty][tk]代表在阿福(tx,ty)这个点上面拿了几个宝石tk。例如一开始阿福(2,3)这个点上面没有宝石所以状态为dp[2][3][0],但是阿福要前往下一个点,需要判重防止下次到达旁边的点后又走回来,所以将dp[2][3][0]赋值为1,…….当到达(4,7)时阿福所拥有的宝石仍为0,所以dp[4][7][0]=1,当到达(3,7)这个点时dp[3][7][2]=1, 为什么是2呢?因为宝石的信息是不一样的,是0号宝石,还是1号宝石,还是2号宝石……!所以用二进制编码的1111代表拥有0,1,2,3四块宝石,用1011代表拥有了0,1,3号宝石!然后将其转换成10进制数字,这样就记录了阿福在每次移动时所携带宝石具体信息(到底是哪几块)。在移动到(2,7)位置时状态转化为dp[2][7][3], 下次再移动到(3,7)这个位置时其状态为dp[3][7][3],所以在两次到达(3,7)的位置时其状态是不一样的<所携带的宝石是不一样的>。在具体实现宝石的获取和判断时,用 | 运算。因为需要集齐k个宝石(假如给了0,1,2,3 四种宝石但是输入的k值为2,所以加了一个check()加以判断)。(摘自:blog.csdn.net/a948433271/article/details/47073971)
import java.util.*;
//http://bailian.openjudge.cn/practice/4105/
public class SavePrincess {
private static Scanner input;
private static final int MAXNUM = 202;
private static final int N = (1 << 5) - 1;
static char[][] maps;
static boolean[][][] visited;
static int R, C, K;
static int[][] transferGateLocation; // 保存传送门的坐标
static int transferGateNumber; // 传送门的数量
static int baoshi; // 整个地图宝石总数,当需要搜集的种类大于它时,是个根本不可能完成的任务!
static int dirx[] = { 0, 0, 1, -1 };
static int diry[] = { 1, -1, 0, 0 };
public static void main(String[] args) {
input = new Scanner(System.in);
int n = input.nextInt();
transferGateLocation = new int[10][2];
maps = new char[MAXNUM][MAXNUM];
int x = 0, y = 0;
while (n != 0) {
for (int i = 0; i < 10; i++)
Arrays.fill(transferGateLocation[i], 0);
transferGateNumber = 0;
baoshi = 0;
visited = new boolean[MAXNUM][MAXNUM][N];
for (int i = 0; i < MAXNUM; i++)
for (int j = 0; j < MAXNUM; j++)
Arrays.fill(visited[i][j], false);
R = input.nextInt();
C = input.nextInt();
K = input.nextInt();
for (int i = 0; i < R; i++) {
String ss = input.next();
maps[i] = ss.toCharArray();
}
for (int i = 0; i < R; i++)
for (int j = 0; j < C; j++) {
if (maps[i][j] == 'S') {
x = i;
y = j;
maps[i][j] = '.';
} else if (maps[i][j] == '$') {
transferGateLocation[transferGateNumber][0] = i;
transferGateLocation[transferGateNumber][1] = j;
transferGateNumber++;
} else if (Character.isDigit(maps[i][j])) {
baoshi++;
} else {
continue;
}
}
// System.out.println("startx:"+x+"-starty:"+y+" baoshi:"+baoshi);
if (K > baoshi) {
n--;
continue;
}
int ans = BFS(x, y);
if (ans == -1) {
System.out.println("oop!");
} else {
System.out.println(ans);
}
n--;
}
}
static boolean check(int tk) { // 核对当前位置是否已经集齐所有宝石
int cnt = 0;
for (int i = 0; i <= 5; i++) {
if (((tk >> i) & 1) == 1)
cnt++;
}
if (cnt >= K)
return true;
else
return false;
}
static int BFS(int x, int y) {
Queue<node> queue = new LinkedList<node>();
visited[x][y][0] = true;
queue.add(new node(x, y, 0, 0));
while (!queue.isEmpty()) {
node n = queue.poll();
for (int i = 0; i < 4; i++) {
int row = n.x + dirx[i]; //注意是n.x,找bug在这里耽误了好久
int col = n.y + diry[i];
if (row < 0 || row >= R || col < 0 || col >= C)
continue;
if (maps[row][col] == '#')
continue;
if (!visited[row][col][n.key]) {
if (maps[row][col] == 'E') {
if (check(n.key))
return n.step + 1;
}
if (Character.isDigit(maps[row][col])) {
visited[row][col][n.key] = true;
int tk = n.key | (1 << (maps[row][col] - '0'));
queue.add(new node(row, col, n.step + 1, tk));
}
if (maps[row][col] == '.') {
visited[row][col][n.key] = true;
queue.add(new node(row, col, n.step + 1, n.key));
}
if (maps[row][col] == '$') {// 传送门
visited[row][col][n.key] = true;
queue.add(new node(row, col, n.step + 1, n.key));
for (int k = 0; k < transferGateNumber; k++) {
int transx = transferGateLocation[k][0];
int transy = transferGateLocation[k][1];
if (!visited[transx][transy][n.key]) {
visited[transx][transy][n.key] = true;
queue.add(new node(transx, transy, n.step + 1, n.key));
}
}
}
}
}
}
return -1;
}
}
class node {
int x;
int y;
int step; // 步数
int key; // 当前的这一步的宝石获得情况
public node(int x, int y, int step, int key) {
this.x = x;
this.y = y;
this.step = step;
this.key = key;
}
}
还好没有内存超限。