地宫取宝——动态规划

地宫取宝

题目描述

X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。 地宫的入口在左上角,出口在右下角。 小明被带到地宫的入口,国王要求他只能向右或向下行走。 走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。 当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。 请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。

输入格式

输入一行3个整数,用空格分开:n m k (1< =n,m< =50, 1< =k< =12)

接下来有 n 行数据,每行有 m 个整数 Ci (0< =Ci< =12)代表这个格子上的宝物的价值

输出格式

要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。

样例输入

2 3 2
1 2 3
2 1 5

样例输出

14

解题思路

采用动态规划的方法,首先确定dp数组含义。因为不仅要考量行列信息,还有考量当前手中的宝物数量,同时宝物拿取存在要求,必须当前格子宝物价值比手中所有宝物价值都大才可以取,所以采用四维数组。

dp[i][j][k][w]表示在当前第i行第j列时手中宝物数量为k,手中宝物的价值不超过w2情况下的种类数。

边界状态

  1. 第一行第一列手中宝物数量为0时,种类数为1.
  2. 第一行第一列手中宝物数量为1时,且手中宝物价值大于第一行第一列的价值种类数为1.

状态转移方程

  1. 不拿,dp[i][j][k][w]=dp[i-1][j][k1][w]+dp[i][j-1][k1][w]
  2. 拿,dp[i][j][k][w]=dp[i-1][j][k1-1][wealth[i][j]]+dp[i][j-1][k1-1][wealth[i][j]],拿当前的第i行第j列的宝物的种类数来自(1)上一行k-1件商品且最大宝物价值不超过当前格子宝物价值的种类数。(2)前一列k-1件商品且最大宝物价值不超过当前格子宝物价值的种类数。
  3. 当前dp[i][j][k][w]种类为拿当前的宝物种类数和不拿当前宝物的种类数之和。

注意:种类数较大,需要时刻注意取模问题。

代码

import java.util.Scanner;

public class Main {
    static final int MOD = 1000000007;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int k = scanner.nextInt();

        //dp[i][j][k][m]---棋盘第i行,第j列上有k件宝物并且宝物最大价值不超过m的种类数
        //创建棋盘宝物价值数组
        int[][] wealth=new int[n+1][m+1];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                wealth[i][j]=scanner.nextInt();
            }
        }

        //创建四维数组并初始化
        int[][][][] dp=new int[n+1][m+1][k+1][13];

        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                for(int k1=0;k1<=k;k1++){
                    for(int w2=0;w2<13;w2++){

                        //边界状态赋值
                        if (i == 1 && j == 1) {
                            //边界状态k1=0 手上没有宝物,不取宝物,则第1行第1列手上宝物为0,最大价值不超过w2的种类数为1
                            //k1=1,w2>wealth[1][1]  在第1行第1列拿取一个宝物,要拿宝物,那么最大价值要超过当前格子的宝物价值,其种类数为1
                            if (k1 == 0 || (k1 == 1 && w2 > wealth[1][1])) {
                                dp[i][j][k1][w2] = 1;
                            }
                            continue;
                        }

                        int num_notake=0;
                        int num_take=0;

                        //拿当前的商品
                        if(k1>0&&w2>wealth[i][j]){
                            num_take=dp[i-1][j][k1-1][wealth[i][j]]%MOD+dp[i][j-1][k1-1][wealth[i][j]] % MOD;
                            num_take%=MOD;
                        }
                        //不取当前商品
                        num_notake=dp[i-1][j][k1][w2]%MOD+dp[i][j-1][k1][w2] % MOD;
                        //这一步很重要,如果不取模,那么结果将会溢出,加数取模后和应该继续取模
                        num_notake%=MOD;

                        dp[i][j][k1][w2]=(num_notake+num_take)%MOD;
                    }
                }
            }
        }
        System.out.println(dp[n][m][k][12]);
    }
}
posted @ 2025-03-15 14:59  狐狸胡兔  阅读(19)  评论(0)    收藏  举报