Under my umbrella.

SKII

Less is more.

蓝桥杯 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];
	}
}

posted @ 2020-03-05 17:37  NLYang  阅读(448)  评论(0编辑  收藏  举报