蓝桥杯 2014年 第五届 迷宫寻宝 详解(JAVA)
蓝桥杯 2014年 第五届 迷宫寻宝 详解(JAVA)
基础思路(DFS)
package provincial_2014B;
import java.util.Scanner;
/**
* 该题有两种做法:
* - dfs + 剪枝: 虽有优化,但还是速度较慢,但好入门
* - 动态规划(记忆性深搜实现):先搞清楚dfs,再了解dp
*
*/
public class Nine {
// 一般都把map数组定义为全局变量
private static int n,m,k;
private static int[][] map;
private static final int MOD = 1000000007;
// 题中告知要对MOD取余,显然较大,所以用long或double
private static long ans;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
n = scan.nextInt();
m = scan.nextInt();
k = scan.nextInt();
map = new int[n][m];
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
map[i][j] = scan.nextInt();
}
}
dfs(0, 0, 0, -1);
System.out.println(ans);
}
// 需要注意的是:x是横坐标,y是纵坐标,所以对应于数组的元素为map[y][x];
private static void dfs(int x, int y, int goods, int max) {
// 边界预防
if(x==m||y==n) {
return;
}
int now = map[y][x];
// 到达最终点
// ***注意:到达最终点后由于不会再进行判断拿不拿了
// *** 所以必须在这里再判断一次拿不拿
if(x==m-1&&y==n-1) {
if(goods==k || (goods==k-1&&max<now)) {
ans++;
ans %= MOD;
}
return;
}
// 拿了,向下或向右走
if(max<now) {
dfs(x, y+1, goods+1, now);
dfs(x+1, y, goods+1, now);
}
// 没拿,向下或向右走
dfs(x+1, y, goods, max);
dfs(x, y+1, goods, max);
}
}
基础思路优化(剪枝)
当goods(已拿的宝物)比最终要拿到的宝物还多时,就return(不走了)。
package provincial_2014B;
import java.util.Scanner;
/**
* 该题有两种做法:
* - dfs + 剪枝: 虽有优化,但还是速度较慢,但好入门
* - 动态规划(记忆性深搜实现):先搞清楚dfs,再了解dp
*
*/
public class Nine {
// 一般都把map数组定义为全局变量
private static int n,m,k;
private static int[][] map;
private static final int MOD = 1000000007;
private static long ans;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
n = scan.nextInt();
m = scan.nextInt();
k = scan.nextInt();
map = new int[n][m];
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
map[i][j] = scan.nextInt();
}
}
dfs(0, 0, 0, -1);
System.out.println(ans);
}
// 需要注意的是:x是横坐标,y是纵坐标,所以对应于数组的元素为map[y][x];
private static void dfs(int x, int y, int goods, int max) {
// 边界预防
if(x==m||y==n||goods>k) {
return;
}
int now = map[y][x];
// 到达最终点
// ***注意:到达最终点后由于不会再进行判断拿不拿了
// *** 所以必须在这里再判断一次拿不拿
if(x==m-1&&y==n-1) {
if(goods==k || (goods==k-1&&max<now)) {
ans++;
ans %= MOD;
}
return;
}
// 拿了,向下或向右走
if(max<now) {
dfs(x, y+1, goods+1, now);
dfs(x+1, y, goods+1, now);
}
// 没拿,向下或向右走
dfs(x+1, y, goods, max);
dfs(x, y+1, goods, max);
}
}
动态规划/记忆型递归
其实记忆型递归就是在刚刚剪枝的基础上再进行优化(剪枝)。
核心就是加入一个新数组cache用来缓存每一个子问题的解。其中,dfs有几个参数,cache就有几个维度。将每种情况看作一种状态。
package provincial_2014B;
import java.util.Scanner;
/**
* 该题有两种做法:
* - dfs + 剪枝: 虽有优化,但还是速度较慢,但好入门
* - 动态规划(记忆性深搜实现):先搞清楚dfs,再了解dp
*
*/
public class Nine {
// 一般都把map数组定义为全局变量
private static int n,m,k;
private static int[][] map;
private static final int MOD = 1000000007;
// 开个数组反映一一映射关系
private static long[][][][] cache;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
n = scan.nextInt();
m = scan.nextInt();
k = scan.nextInt();
map = new int[n][m];
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
map[i][j] = scan.nextInt();
}
}
//***注意:因为宝物的价值一开始传进去为-1,不满足下标规则
//*** 所以记录时要+1,所以最大下标也要+1
cache = new long[n][m][14][14];
// 为什么填充-1:因为ans有可能return0,在不满足条件时,
// 就可以看做该点已经走过,状态为0
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
for(int r = 0; r < 14; r++)
for(int q = 0; q < 14; q++)
cache[i][j][r][q]=-1;
long ans = dp(0, 0, 0, -1);
System.out.println(ans);
}
private static long dp(int x, int y, int goods, int max) {
// 边界预防+剪枝
if(x==m||y==n||goods>k) {
return 0;
}
if(cache[y][x][goods][max+1]!=-1) return cache[y][x][goods][max+1];
int now = map[y][x];
// 某个点的返回值(该点的状态/该点拿了几件货物)
// 由以下情况组成
long ans = 0;
// 到达最终点
// ***注意:到达最终点后由于不会再进行判断拿不拿了
// *** 所以必须在这里再判断一次拿不拿
if(x==m-1&&y==n-1) {
if(goods==k || (goods==k-1&&max<now)) {
return 1;
}
return 0;
}
// 拿了,向下或向右走
if(max<now) {
ans += dp(x, y+1, goods+1, now);
ans += dp(x+1, y, goods+1, now);
}
// 没拿,向下或向右走
ans += dp(x+1, y, goods, max);
ans += dp(x, y+1, goods, max);
// 记录该点的值到缓存
cache[y][x][goods][max+1] = ans % MOD;
return cache[y][x][goods][max+1];
}
}