滴滴笔试题

小青蛙用仅剩的体力值P走地下迷宫。迷宫为一个n*m的grid,每个位置为0或1,1代表可以走,0代表无法走。初始节点为(0,0),目的节点为(0,m - 1)(保证初始和目的都为1)。青蛙往上走时消耗体力值3,水平走消耗1,往下走不消耗体力值。计算青蛙能否用体力值P跳出迷宫。若不能跳出,输出“can not escape”,否则输出路径。

输入顺序为n,m,p,接着n行m列是gird里的数。

示例:

4 4 10

1 0 0 1

1 1 0 1

0 1 1 1 

0 0 1 1

输出:

【0,0】,【1,0】,【1,1】,【2,1】,【2,2】【2,3】【1,3】【0,3】

不知道别人咋做的(感觉自己做的好复杂 = =),看到这题想起了曾做过的“积水的城市”,用的SPFA,就是把每个节点a的邻接节点保存在该节点的List adjacience中,每个邻接节点都有一个p值,代表从a走到每个邻接点需要消耗的体力值。然后通过遍历,计算从源节点出发到每个节点的最小消耗体力值minp,用v[i][j].minp表示。最后判断目的节点的minp是否小于等于p,若是,则输出。

输出的时候,用到了遍历时加的last(last也是一个节点)。当某节点改变minp,则它的上一个节点last,必然是导致这个最小minp的上一节点,也必然是路径的上一节点。最后从目的节点倒序输出即可。

class Vertex {
	int a; //节点的横坐标
	int b; // 纵坐标
	int p = 0;// 每个节点通过adjacience到达邻节点需要消耗p
	List adjacience; //邻节点
	boolean isInQueue;
	int minp = Integer.MAX_VALUE / 2;
	Vertex last; // 前一个节点
	public Vertex(int i ,int j,int p) {
		this.a = i;
		this.b = j;
		this.p += p;
		this.isInQueue = false;
		this.adjacience = new ArrayList();
	}
}
public class Main {
	static int n;
	static int m;
	static int p;
	static int[][] grid;
	static Vertex[][] v;
	
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		m = in.nextInt();
		p = in.nextInt();
		grid = new int[n][m];
		for (int i = 0; i< n; i++) {
			for(int j = 0; j< m; j++) {
				grid[i][j] = in.nextInt();
			}
		}
		if (minP(grid) > p) System.out.println("can not escape !");
		else print_ans(v[0][m - 1]);
		in.close();
	}
	
	public static int minP(int[][] a) { 
		v = new Vertex[n][m];
		for (int i = 0; i< n; i++) {
			for(int j = 0; j< m; j++) {
				if (a[i][j] == 1) {
					v[i][j] = new Vertex(i, j, 0);
					if (i > 0 && a[i - 1][j] == 1) {
						v[i][j].adjacience.add(new Vertex(i - 1, j, 3)); // 往上走,消耗体力3
					}
					if (j > 0 && a[i][j - 1] == 1) {
						v[i][j].adjacience.add(new Vertex(i, j - 1, 1)); // 往左走
					}
					if (i < n - 1 && a[i + 1][j] == 1) {
						v[i][j].adjacience.add(new Vertex(i + 1, j, 0)); //往下
					}
					if (j < m - 1 && a[i][j + 1] == 1) {
						v[i][j].adjacience.add(new Vertex(i, j + 1, 1)); //往右
					}	
				}
			}
		}
		
		Queue queue = new LinkedList();
		v[0][0].minp = 0;
		queue.add(v[0][0]);
		v[0][0].isInQueue = true;
		Vertex former = null; // former保证了路径不走回头路,比如a节点的邻节点为b,遍历b的邻节点时要排除掉a点
		
		while(!queue.isEmpty()) {
			Vertex s = queue.poll();
			s.isInQueue = false;
			for (int i = 0; i < v[s.a][s.b].adjacience.size(); i++) {
				
				Vertex b =  v[s.a][s.b].adjacience.get(i);
				if(former == null || (b.a != former.a || b.b != former.b)) {
					if (v[b.a][b.b].minp > v[s.a][s.b].minp + b.p) { // 注意此处必须为b.p,因为v[i][j]的p为0
						v[b.a][b.b].minp = v[s.a][s.b].minp + b.p;
						v[b.a][b.b].last = v[s.a][s.b];
						if(!b.isInQueue) {
							b.isInQueue = true;
							queue.add(b);
						}
					} 
				}
				former= s;
			}
		}
		return v[0][m - 1].minp;
	}
	// 打印结果
	public static void print_ans(Vertex v) {
		boolean s = true;
		List re = new ArrayList();
		while (s) {
			Integer[] result = new Integer[2];
			result[0] = v.a;
			re.add(result);
			result[1] = v.b;
			if (v.a == 0 && v.b == 0) 
				s = false;
			v = v.last;
		}
		for (int i = re.size() - 1; i > 0; i--) {
			System.out.print("[" + re.get(i)[0] + "," + re.get(i)[1] + "]" + ",");
		}
		System.out.println("[" + re.get(0)[0] + "," + re.get(0)[1] + "]");
	}
}


你可能感兴趣的:(算法题)