• 蓝桥杯历届试题 地宫取宝 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
    ", 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
    ", ans);
        }
        return 0;
    }
    

      

  • 相关阅读:
    git代码合并与冲突
    jQuery 事件方法---vvvv0
    原生JS获取HTML DOM元素的方法----------c
    JQuery获取元素的方法总结--ccc
    zookeeper[5] zookeeper集群配置及伪集群配置
    zookeeper[4] 安装windows zookeeper,及问题处理
    zookeeper[3] zookeeper API开发注意事项总结
    zookeeper[2] zookeeper原理(转)
    zookeeper[1] (转)ZooKeeper Programmer's Guide(zookeeper编程向导)---中文
    Java[1] Java学习书籍汇总(转)
  • 原文地址:https://www.cnblogs.com/icode-girl/p/5522669.html
Copyright © 2020-2023  润新知