试题 算法提高 密室逃脱 java 题解 1183

30 篇文章 1 订阅
25 篇文章 1 订阅

问题描述

  真人版密室逃脱游戏风靡全球,不仅在麻瓜世界广受欢迎,而且在魔法世界也十分流行。考虑到魔法世界的人们会使用能够瞬间移动的魔法,密室逃脱游戏在被引进魔法世界时作了一些修改:“密室迷宫”由排成n行m列的nm间房间组成,每间房间会被标记为“危险的”或者“安全的”,参加者在左上角的房间中开始游戏,通过使用红绿蓝三种不同的魔法在房间迷阵中移动(只能移动到“安全的”房间,不能移动到“危险的”房间),最后到达右下角的房间即获得胜利。三种不同魔法的效果如下:
  “红魔法”(r):瞬间移动到所在房间右边的第二间房;
  “绿魔法”(g):瞬间移动到所在房间右下方的房间;
  “蓝魔法”(b):瞬间移动到所在房间下方的第三间房;
  魔法师小L最近也迷上了这款游戏,他在游戏开始前拿到了房间地图(“安全的”房间用1标记,“危险的”房间用0标记),并被告知只能使用a次红魔法,b次绿魔法和c次蓝魔法(数据保证n=1+b+3*c;m=1+b+2*a),那么请聪明的你告诉小L,他能不能胜利?如果可以,该怎么使用魔法才能安全的到达右下角的房间?

输入格式

  输入第一行为五个整数n、m、a、b、c,用空格隔开;
  第二行到第n+1行每行m个整数(0或1),表示房间地图(数据保证地图左上角和右下角的整数为1)

输出格式

  若小L不能够到达终点,则输出-1;
  若小L能够到达终点,则输出字典序最大的使用的魔法序列(用r、g、b表示,不用空格空行)。

样例输入

12 9 3 2 3
1 1 1 1 1 0 0 1 1
0 1 1 0 0 1 1 1 1
1 1 1 1 1 0 0 0 1
1 0 1 1 0 1 1 0 1
1 1 1 1 1 0 1 1 0
1 1 1 1 1 1 0 1 1
0 1 1 0 0 1 1 1 0
0 0 0 0 1 0 1 1 1
0 1 1 1 1 1 1 1 1
1 1 0 0 1 1 0 1 1
1 1 1 1 1 1 0 1 0
0 1 1 1 1 1 0 1 1

样例输出

rrgrgbbb

数据规模和约定

  1≤n,m≤1000


解题思路:

同迷宫问题类似,不同的是,限制了走法的规则只有固定三种。

地图的存储方式:用二维数组存储,但为了在“走迷宫”时,在判断某处能不能走时防止数组越界,还需要按走法的规则适当扩充行列。

另外,输出的走法方式还需要满足字典序最大,只需在深搜时,按字典序从大到小搜索即可,(此题不从在“rrggrb”比“rrggr”字典序大这类情况,因为既然能到达终点,肯定不能在当前序列上再走了,会超过终点)。深搜出的第一种情况一定是最终答案。这也是为什么尽管地图会很大,但递归任然不会超时的原因,因为只需求出第一种情况。

题目还限制了走法的次数,即不能超出这些次数,可以用一个访问数组存储各种走法的次数。每走一次,便将访问数组的对应值减一,当然在回溯时还需再加回去。

java代码:

import java.io.*;

public class Main {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String[] split = br.readLine().split(" ");
		int n = Integer.parseInt(split[0]);
		int m = Integer.parseInt(split[1]);
		int r = Integer.parseInt(split[2]);
		int g = Integer.parseInt(split[3]);
		int b = Integer.parseInt(split[4]);
		int [][]flag = new int[n + 4][m + 3];//第零行零列空出去,再需按规则考虑防止数组越界
		for(int i = 1; i <= n; i++) {
			split = br.readLine().split(" ");
			for(int j = 0; j < split.length; j++) {
				flag[i][j + 1] = Integer.parseInt(split[j]);
			}
		}
		Temp1183 obj = new Temp1183(flag, r, g, b);
		obj.dfs(1, 1);//从第一行第一列开始搜索
		System.out.println("-1");//说明之前没到达终点,需要输出-1
	}
}
class Temp1183{
	int [][]flag;
	int n;//数组有效行数
	int m;//数组有效列数
	int []arr = new int[4];//下标从1开始,依次表示r,g,b的最大次数
	StringBuilder builder = new StringBuilder();//拼接最终序列
	
	public Temp1183(int[][] flag, int r, int g, int b) {
		this.flag = flag;
		n = flag.length - 4;
		m = flag[0].length - 3;
		arr[1] = r;
		arr[2] = g;
		arr[3] = b;
	}
	
	public void dfs(int a, int b) {
		if(a == n && b == m) {//表示已经到达终点,此时的builder中即为最终答案
			System.out.println(builder.toString());
			System.exit(0);//直接退出程序
		}
		if(a > n || b > m) {//表示已超出地图范围,返回上一层
			return;
		}
		for(int i = 1; i <= 3; i++) {
			if(arr[i] > 0) {//还有次数
				if(i == 1) {
					if(flag[a][b + 2] == 0)continue;//此处危险,不能进入
					b = b + 2;//r的移动方式
					builder.append('r');//拼接r
					arr[1]--;//r的次数少一次
					dfs(a, b);//从此处继续往下搜
					arr[1]++;//回溯
					b = b - 2;
					builder.deleteCharAt(builder.length() - 1);
				}else if(i == 2) {
					if(flag[a + 1][b + 1] == 0)continue;
					a += 1;
					b += 1;
					builder.append('g');
					arr[2]--;
					dfs(a, b);
					arr[2]++;
					a -= 1;
					b -= 1;
					builder.deleteCharAt(builder.length() - 1);
				}else {
					if(flag[a + 3][b] == 0)continue;
					a += 3;
					builder.append('b');
					arr[3]--;
					dfs(a, b);
					arr[3]++;
					a -= 3;
					builder.deleteCharAt(builder.length() - 1);
				}
			}
		}
	}
}

提交截图:

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值