蓝桥杯历届试题 地宫取宝 dp or 记忆化搜索

Posted 小小八

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝桥杯历届试题 地宫取宝 dp or 记忆化搜索相关的知识,希望对你有一定的参考价值。

问题描述
  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
 
暴力dfs会超时,可以dp或者记忆化搜索....理解可以...自己写可能就....gg了...
这样的话,感觉还是记忆化搜索更萌一些,因为不用考虑那么多边界...
思路见代码.
dp:
/*
蓝桥杯历届试题地宫寻宝

dp
状态:dp[i][j][num][val] 表示从起点(1, 1)走到(i, j), 已经取了num个宝物,最大价值是val 的方案数。
初态:dp[1][1][0][0] = 1; dp[1][1][1][mp[1][1]] = 1;
转移方程:由上方或者左方的格子转移而来,详见代码;
*/

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;

#define mod 1000000007
int dp[55][55][15][15];
int mp[55][55];

int main() {
    int n, m, k;
    while(~scanf("%d%d%d", &n, &m, &k)) {
        for (int i=1; i<=n; ++i) {
            for (int j=1; j<=m; ++j) {
                scanf("%d", &mp[i][j]);
            }
        }

        memset(dp, 0, sizeof(dp));
        dp[1][1][0][0] = 1;
        dp[1][1][1][mp[1][1]] = 1;

        for (int i=1; i<=n; ++i) {
            for (int j=1; j<=m; ++j) {
                dp[i][j][0][0] += (dp[i-1][j][0][0] + dp[i][j-1][0][0]);
                dp[i][j][0][0] %= mod;
                for (int num=1; num<=k; ++num) {
                    for (int val=0; val<=12; ++val) {
                        dp[i][j][num][val] += (dp[i-1][j][num][val] + dp[i][j-1][num][val]);
                        dp[i][j][num][val] %= mod;
                    }
                    if (num == 1) {
                        dp[i][j][1][mp[i][j]] += dp[i-1][j][0][0];
                        dp[i][j][1][mp[i][j]] %= mod;
                        dp[i][j][1][mp[i][j]] += dp[i][j-1][0][0];
                        dp[i][j][1][mp[i][j]] %= mod;
                    }
                    else {
                        for (int t=0; t<mp[i][j]; ++t) {
                            dp[i][j][num][mp[i][j]] += dp[i-1][j][num-1][t];
                            dp[i][j][num][mp[i][j]] %= mod;
                            dp[i][j][num][mp[i][j]] += dp[i][j-1][num-1][t];
                            dp[i][j][num][mp[i][j]] %= mod;
                        }
                    }
                }
            }
        }

        int ans = 0;
        for (int i=0; i<=12; ++i) {
            ans += dp[n][m][k][i];
            ans %= mod;
        }
        printf("%d\n", ans);
    }
    return 0;
}

记忆化搜索:

 

/*
蓝桥杯历届试题 地宫取宝

dp[i][j][num][k] 表示到位置(i, j)时, 取了第num个宝藏,最大宝藏值是k时,
能到终点的路线方案数。

dfs超时。
记忆化搜索...
*/


#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
#define mod 1000000007

int dp[55][55][15][15];
int n, m, k;
int mp[55][55];

int dfs(int nowx, int nowy, int cnt, int nowMax) {
    if (dp[nowx][nowy][cnt][nowMax+1] != -1) {
        return dp[nowx][nowy][cnt][nowMax+1];
    }
    int ans = 0;
    if (nowx == n-1 && nowy == m-1) {
        if (mp[nowx][nowy] > nowMax) {
            if (cnt == k || cnt == k-1)
                ans++;
            ans %= mod;
        }
        else if (cnt == k) ans++;
        ans %= mod;
        return dp[nowx][nowy][cnt][nowMax+1] = ans;
    }

    if (nowx+1 < n) {
        if (mp[nowx][nowy] > nowMax) {
            ans += dfs(nowx+1, nowy, cnt+1, mp[nowx][nowy]);
            ans %= mod;
        }
        ans += dfs(nowx+1, nowy, cnt, nowMax);
        ans %= mod;
    }
    if (nowy+1 < m) {
        if (mp[nowx][nowy] > nowMax) {
            ans += dfs(nowx, nowy+1, cnt+1, mp[nowx][nowy]);
            ans %= mod;
        }
        ans += dfs(nowx, nowy+1, cnt, nowMax);
        ans %= mod;
    }
    return dp[nowx][nowy][cnt][nowMax+1] = ans;
}

int main() {
    while(~scanf("%d%d%d", &n, &m, &k)) {
        memset(dp, -1, sizeof(dp));
        for (int i=0; i<n; ++i) {
            for (int j=0; j<m; ++j) {
                scanf("%d", &mp[i][j]);
            }
        }
        int ans = dfs(0, 0, 0, -1);
        printf("%d\n", ans);
    }
    return 0;
}

 

  

 

以上是关于蓝桥杯历届试题 地宫取宝 dp or 记忆化搜索的主要内容,如果未能解决你的问题,请参考以下文章

蓝桥杯 --- 数学与简单DP(习题)

蓝桥杯真题训练 五一 2/5

蓝桥杯 地宫取宝(12')

蓝桥杯 地宫寻宝 带缓存的DFS

蓝桥杯-地宫取宝

地宫取宝----第五届蓝桥杯