问题描述
真人版密室逃脱游戏风靡全球,不仅在麻瓜世界广受欢迎,而且在魔法世界也十分流行。考虑到魔法世界的人们会使用能够瞬间移动的魔法,密室逃脱游戏在被引进魔法世界时作了一些修改:“密室迷宫”由排成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);
}
}
}
}
}
提交截图: