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

猜你喜欢

转载自www.cnblogs.com/fromneptune/p/12421485.html