已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。
鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。
如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。
请问,鸣人要追上佐助最少需要花费多少时间?
输入
输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
输出
输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。
样例输入1
4 4 1
#@##
**##
###+
****
样例输入2
4 4 2
#@##
**##
###+
****
样例输出1
6
样例输出2
4
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
static int R, C, T;
static char[][] grid;
static boolean[][] vis;
final static int[][] dir = { {1,0},{0,-1},{0,1},{-1,0} };
static boolean inArea(int x, int y) {
return x >= 0 && x < R && y >= 0 && y < C;
}
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
R = sc.nextInt();
C = sc.nextInt();
T = sc.nextInt();
grid = new char[R][C];
vis = new boolean[R][C];
int sx = 0, sy= 0;
for (int i = 0; i < R; i++) {
String s = sc.next();
for (int j = 0; j < C; j++) {
grid[i][j] = s.charAt(j);
if (grid[i][j] == '@') {
sx = i; sy = j;
}
}
}
int res = bfs(sx, sy);
System.out.println(res);
}
private static int bfs(int sx, int sy) {
Queue<Pos> q = new PriorityQueue<>((e1, e2) -> {
if (e1.step == e2.step) {
return e1.gem - e2.gem;
}
return e1.step - e2.step;
});
q.add(new Pos(sx, sy, 0, 0));
vis[sx][sy] = true;
while (!q.isEmpty()) {
Pos t = q.poll();
for (int k = 0; k < 4; k++) {
int tx = t.x + dir[k][0];
int ty = t.y + dir[k][1];
if (!inArea(tx, ty) || vis[tx][ty])
continue;
vis[tx][ty] = true;
if (grid[tx][ty] == '*'){
q.add(new Pos(tx, ty, t.step+1, t.gem));
}else if (grid[tx][ty] == '#' && t.gem + 1 <= T) {
q.add(new Pos(tx, ty, t.step+1, t.gem+1));
} else if (grid[tx][ty] == '+') {
return t.step + 1;
}
}
}
return -1;
}
static class Pos {
int x, y, step, gem;
public Pos(int _x, int _y, int _step, int gem) {
this.x = _x;
this.y = _y;
this.step = _step;
this.gem = gem;
}
}
}
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
static int R, C, T;
static char[][] grid;
static boolean[][][] vis;
final static int[][] dir = { {1,0},{0,-1},{0,1},{-1,0} };
static boolean inArea(int x, int y) {
return x >= 0 && x < R && y >= 0 && y < C;
}
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
R = sc.nextInt();
C = sc.nextInt();
T = sc.nextInt();
grid = new char[R][C];
vis = new boolean[R][C][T+1];
int sx = 0, sy= 0;
for (int i = 0; i < R; i++) {
String s = sc.next();
for (int j = 0; j < C; j++) {
grid[i][j] = s.charAt(j);
if (grid[i][j] == '@') {
sx = i; sy = j;
}
}
}
int res = bfs(sx, sy);
System.out.println(res);
}
private static int bfs(int sx, int sy) {
Queue<Pos> q = new ArrayDeque<>();
q.add(new Pos(sx, sy, 0, 0));
vis[sx][sy][0] = true;
while (!q.isEmpty()) {
Pos t = q.poll();
if (grid[t.x][t.y] == '+') {
return t.step;
}
for (int k = 0; k < 4; k++) {
int tx = t.x + dir[k][0];
int ty = t.y + dir[k][1];
if (!inArea(tx, ty))
continue;
int gem = t.gem;
if (grid[tx][ty] == '#') {
gem++;
}
if (gem <= T && !vis[tx][ty][gem]) {
q.add(new Pos(tx, ty, t.step+1, gem));
vis[tx][ty][gem] = true;
}
}
}
return -1;
}
static class Pos {
int x, y, step, gem;
public Pos(int _x, int _y, int _step, int gem) {
this.x = _x;
this.y = _y;
this.step = _step;
this.gem = gem;
}
}
}
少写一个类和队列带来了方便,但是深搜把所有情况都列举出来带来的就是超时… 只能得 1 5 \cfrac{1}{5} 51 的分。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
static int R, C, T;
static char[][] grid;
static boolean[][] vis;
final static int[][] dir = { {1,0},{0,-1},{0,1},{-1,0} };
static int INF = 0x3f3f3f3f;
static boolean inArea(int x, int y) {
return x >= 0 && x < R && y >= 0 && y < C;
}
static int min = INF;
private static void dfs(int x, int y, int step, int gem) {
if (grid[x][y] == '+') {
min = Math.min(min, step);
return;
}
for (int k = 0; k < 4; k++) {
int tx = x + dir[k][0];
int ty = y + dir[k][1];
if (!inArea(tx, ty) || vis[tx][ty])
continue;
if (grid[tx][ty] == '#') {
gem++;
if (gem > T) return;
}
vis[tx][ty] = true;
dfs(tx, ty, step+1, gem);
vis[tx][ty] = false;
}
}
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
R = sc.nextInt();
C = sc.nextInt();
T = sc.nextInt();
grid = new char[R][C];
vis = new boolean[R][C];
int sx = 0, sy= 0;
for (int i = 0; i < R; i++) {
String s = sc.next();
for (int j = 0; j < C; j++) {
grid[i][j] = s.charAt(j);
if (grid[i][j] == '@') {
sx = i; sy = j;
}
}
}
dfs(sx, sy, 0, 0);
System.out.println(min == INF ? -1 : min);
}
}
设当前位置为 (tx, ty)
,终点为 (ex, ey),那么有 MHD 得,至少还需要走 least = |ex-tx| + |ey - ty|
步,也就是说,如果 step + least >= min
返回即可。
用记忆化搜索的思想,使用 int[][][] dist
标记每一个点的宝石使用情况.
答案错误:只过了 8 组数据,原来前面的程序也是错误的,第二个样例跑出来竟是 6。这是因为你对参数 tem 进行了 tem++,然后又作为参数传递进去,导致回溯时无法撤回。
只得了 18 20 \cfrac{18}{20} 2018 的分:不知为何…
import java.util.*;
import java.math.*;
import java.io.*;
public class Main {
static int R, C, T;
static char[][] grid;
static boolean[][] vis;
final static int[][] dir = { {1,0},{0,-1},{0,1},{-1,0} };
static int INF = 0x3f3f3f3f;
static int ex, ey;
static int[][][] dist;
static boolean inArea(int x, int y) {
return x >= 0 && x < R && y >= 0 && y < C;
}
static int min = INF;
private static void dfs(int x, int y, int step, int gem) {
if (grid[x][y] == '+') {
min = Math.min(min, step);
return;
}
if (step < dist[x][y][gem]) {
dist[x][y][gem] = step;
} else {
return;
}
for (int k = 0; k < 4; k++) {
int tx = x + dir[k][0];
int ty = y + dir[k][1];
if (!inArea(tx, ty) || vis[tx][ty])
continue;
if (step + Math.abs(ex-tx) + Math.abs(ey-ty) >= min)
continue;
int t = gem;
if (grid[tx][ty] == '#') {
t++;
if (t > T) return;
}
vis[tx][ty] = true;
dfs(tx, ty, step+1, t);
vis[tx][ty] = false;
}
}
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
R = sc.nextInt();
C = sc.nextInt();
T = sc.nextInt();
grid = new char[R][C];
vis = new boolean[R][C];
dist = new int[R][C][T+1];
int sx = 0, sy= 0;
for (int i = 0; i < R; i++) {
String s = sc.next();
for (int j = 0; j < C; j++) {
grid[i][j] = s.charAt(j);
if (grid[i][j] == '@') {
sx = i; sy = j;
} else if (grid[i][j] == '+') {
ex = i; ey = j;
}
for (int k = 0; k <= T; k++) {
dist[i][j][k] = INF;
}
}
}
vis[sx][sy] = true;
dfs(sx, sy, 0, 0);
System.out.println(min == INF ? -1 : min);
}
}
需要注意的问题:一定要注意语句的后效性,有些判断是不能放在另一些判断后面的…
private static void dfs(int x, int y, int step, int gem) {
if (gem > T)
return;
if (step < dist[x][y][gem]) {
dist[x][y][gem] = step;
} else {
return;
}
if (step + Math.abs(ex-x) + Math.abs(ey-y) >= min)
return;
for (int k = 0; k < 4; k++) {
int tx = x + dir[k][0];
int ty = y + dir[k][1];
if (!inArea(tx, ty) || vis[tx][ty])
continue;
vis[tx][ty] = true;
if (grid[tx][ty] == '#') dfs(tx, ty, step+1, gem+1);
if (grid[tx][ty] == '*') dfs(tx, ty, step+1, gem);
if (grid[tx][ty] == '+') min = Math.min(min, step+1);
vis[tx][ty] = false;
}
}