地宫取宝

问题描述
  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 2 2
1 2
2 1
样例输出
2
样例输入
2 3 2
1 2 3
2 1 5
样例输出
14
 
这道题目是要求找到满足条件的行动方案,一条路径可能还有多个方案,到了某个点,是有选择,取或者不取,取必须得保证当前物品比你包包里的任何一个都大,因此需要记录最大值,求的是正好为k个的方案,要不多不少,所以可以dfs进行,同时记录宝贝数和当前最大值,拿够了k个,后面就不要拿了,拿了也是白拿,因为不满足条件嘛,直接dfs是会超时的,想想最后结果都得取模,不超时才怪。
超时代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define mod 1000000007
using namespace std;
int n,m,k,c;
int mp[50][50];
void dfs(int x,int y,int max_,int cc) {
    if(x == n - 1 && y == m - 1) {
        c += cc == k || cc == k - 1 && mp[x][y] > max_;
        return;
    }
    if(x < n - 1) {
        if(cc < k && mp[x][y] > max_) dfs(x + 1,y,mp[x][y],cc + 1);
        dfs(x + 1,y,max_,cc);
    }
    if(y < m - 1) {
        if(cc < k && mp[x][y] > max_) dfs(x,y + 1,mp[x][y],cc + 1);
        dfs(x,y + 1,max_,cc);
    }
}
int main() {
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 0;i < n;i ++) {
        for(int j = 0;j < m;j ++) {
            scanf("%d",&mp[i][j]);
            mp[i][j] ++;
        }
    }
    dfs(0,0,0,0);
    printf("%d",c);
}

仔细想想,从某个点到结尾的路是确定那么几条的,因为只允许向右和下走嘛,我们可以记录每个点,对应某个最大值以及相应手中宝贝个数的方案数,这样前面的点访问到后面的点时就可以直接用存的方案数去进行匹配衔接。dfs有四个参数,坐标以及手中最大值和手中宝贝数,显然在点(x,y)它的状态与max_和cc有关,而这种状态来自前面的路径,也就是说只要前面的路径情况满足最大值时max_,且手中宝贝数时cc,且到了点(x,y),而点(x,y)对此状态有存,就可以直接使用返回。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define mod 1000000007
using namespace std;
int n,m,k;
int mp[51][51];
int sta[51][51][14][14];
int dfs(int x,int y,int max_,int cc) {
    if(sta[x][y][max_][cc] != -1) return sta[x][y][max_][cc];
    if(x == n - 1 && y == m - 1) {
        return sta[x][y][max_][cc] = (cc == k || cc == k - 1 && mp[x][y] > max_);
    }
    int sum = 0;
    if(x < n - 1) {
        if(cc < k && max_ < mp[x][y]) {
            sum = (sum + dfs(x + 1,y,mp[x][y],cc + 1)) % mod;
        }
        sum = (sum + dfs(x + 1,y,max_,cc)) % mod;
    }
    if(y < m - 1) {
        if(cc < k && max_ < mp[x][y]) {
            sum = (sum + dfs(x,y + 1,mp[x][y],cc + 1)) % mod;
        }
        sum = (sum + dfs(x,y + 1,max_,cc)) % mod;
    }
    return sta[x][y][max_][cc] = sum;
}
int main() {
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 0;i < n;i ++) {
        for(int j = 0;j < m;j ++) {
            scanf("%d",&mp[i][j]);
            mp[i][j] ++;///避开0
        }
    }
    memset(sta,-1,sizeof(sta));
    dfs(0,0,0,0);
    printf("%d",sta[0][0][0][0]);
}

既然可以用记忆化搜索,那么可以用动态规划做一下。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define mod 1000000007
using namespace std;
int n,m,k;
int mp[51][51];
int dp[51][51][14][14];int main() {
    scanf("%d%d%d",&n,&m,&k);
    int max_ = 0;
    for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= m;j ++) {
            scanf("%d",&mp[i][j]);
            mp[i][j] ++;///避开0
            max_ = max(max_,mp[i][j]);
        }
    }
    dp[1][1][1][mp[1][1]] = 1;
    dp[1][1][0][0] = 1;
    for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= m;j ++) {
            for(int l = 0;l <= k;l ++) {
                for(int o = 0;o <= max_;o ++) {
                    if(i + 1 <= n) {
                        dp[i + 1][j][l][o] = (dp[i + 1][j][l][o] + dp[i][j][l][o]) % mod;
                        if(mp[i + 1][j] > o) {
                            dp[i + 1][j][l + 1][mp[i + 1][j]] = (dp[i + 1][j][l + 1][mp[i + 1][j]] + dp[i][j][l][o]) % mod;
                        }
                    }
                    if(j + 1 <= m) {
                        dp[i][j + 1][l][o] = (dp[i][j + 1][l][o] + dp[i][j][l][o]) % mod;
                        if(mp[i][j + 1] > o) {
                            dp[i][j + 1][l + 1][mp[i][j + 1]] = (dp[i][j + 1][l + 1][mp[i][j + 1]] + dp[i][j][l][o]) % mod;
                        }
                    }
                }
            }
        }
    }
    int ans = 0;
    for(int i = 0;i <= max_;i ++) {
        ans = (ans + dp[n][m][k][i]) % mod;
    }
    printf("%d",ans);
}

 java:

import java.util.Arrays;
import java.util.Scanner;
public class Main {
    private static Scanner sc = new Scanner(System.in);
    private static final int mod = 1000000007;
    private static int n,m,k;
    private static int [][] mp = new int[51][51];
    private static int [][][][] sta = new int[51][51][14][14];
    private static int dfs(int x,int y,int max_,int cc) {
        if(sta[x][y][max_][cc] != -1) return sta[x][y][max_][cc];
        if(x == n - 1 && y == m - 1) {
            return sta[x][y][max_][cc] = (cc == k || cc == k - 1 && mp[x][y] > max_) ? 1 : 0;
        }
        int sum = 0;
        if(x < n - 1) {
            if(cc < k && max_ < mp[x][y]) {
                sum = (sum + dfs(x + 1,y,mp[x][y],cc + 1)) % mod;
            }
            sum = (sum + dfs(x + 1,y,max_,cc)) % mod;
        }
        if(y < m - 1) {
            if(cc < k && max_ < mp[x][y]) {
                sum = (sum + dfs(x,y + 1,mp[x][y],cc + 1)) % mod;
            }
            sum = (sum + dfs(x,y + 1,max_,cc)) % mod;
        }
        return sta[x][y][max_][cc] = sum;
    }
    public static void main(String[] args) {
        n = sc.nextInt();
        m = sc.nextInt();
        k = sc.nextInt();
        for(int i = 0;i < n;i ++) {
            for(int j = 0;j < m;j ++) {
                for(int l = 0;l < 13;l ++) {
                    Arrays.fill(sta[i][j][l], -1);
                }
                mp[i][j] = sc.nextInt() + 1;
            }
        }
        dfs(0,0,0,0);
        System.out.println(sta[0][0][0][0]);
    }
}
原文地址:https://www.cnblogs.com/8023spz/p/10488667.html