• 蓝桥杯 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];
    	}
    }
    
    
  • 相关阅读:
    CODE[VS] 2506 可恶的体育老师
    CODE[VS] 3411 洪水
    CODE[VS] 2692 小明过生日
    CODE[VS] 2291 糖果堆
    CODE[VS] 2008 你已经爱我多久了
    忽然之间
    Amazing grace 奇异恩典
    无处安放
    AC日记
    AC日记
  • 原文地址:https://www.cnblogs.com/fromneptune/p/12421485.html
Copyright © 2020-2023  润新知