[蓝桥杯][2014年第五届真题]地宫取宝

本文介绍了一个寻宝游戏问题的解决方案,玩家需要从地宫的左上角出发,通过特定路径收集指定数量的宝物。文章提供了详细的算法思路,包括DFS深度优先搜索与记忆化搜索相结合的方法,以高效地计算出所有可能的有效行动方案。

题目描述

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

题意分析:有一个用二维数组表示的地宫,每一个格子处放着一个宝贝,小明从地宫左上角出发,到地宫右下角离开地宫的途中可以(也可以不拿)拿起格子中的宝物(当前格子的宝物价值高于小明手里任意一件宝物的价值),并且小明只能往下走或者往右走,如果离开地宫是小明手中的宝物正好是k件时认为是一个有效的行动方案,现在求所有有效行动方案的数量。 

 

思路分析:

1.仅使用DFS,只用DFS会超时。从出发点开始使用DFS,因为只能往下走或者往右走所以不存在走回来的情况,不需要使用标记数组,在每个格子处如果当前格子里的宝物价值比小明手里任意一件宝物的价值都要大,此时小明有两种选择,一是拿起这个宝物,二是不拿这个宝物,当小明走到地宫出口时,小明手里宝物为k件,有效行动方案计数加一。

 

 

2.使用DFS+记忆化搜索。小明的状态有四个维度分别是x坐标、y坐标、当前手中的宝物数量和手中宝物价值中的最大值。所以,要想记录某个格子下小明的有效方案数不仅要记录格子的位置,还要记录当前手中的宝物数量和手中宝物价值中的最大值,因此使用一个四位维数组dp来存储有效方案数。四位数组中的每一个单位存储的是当前状态(x,y,num,maxValue)下的有效方案数。详细思路见代码.

这里还有一个易错点:

宝物中有价值为0的宝物,所以初始DFS时maxValue需设置为-1,但是这样的话,dp数组就会数组越界,所以在存储有效方案数时,令每个状态下的maxValue加一,这样最后dp[1][1][0][0]里存的就是总有效方案数。

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int mp[55][55];
const int Q = 1000000007;
int dx[] = {0,1};
int dy[] = {1,0};

long long dp[55][55][13][13];//存储每种状态(x,y,num,maxValue)下的有效方案数
long long dfs(int x,int y,int num,int maxValue)
{
    
    //如果当前状态有效方案数已经求过了就直接返回,不再进行递归。
    if(dp[x][y][num][maxValue+1]!=-1)
    {
        return dp[x][y][num][maxValue+1];
    }
    if(x==n && y==m)
    {
        //到达地宫出口时有效的行动方案满足的条件
        if(num==k || (num==k-1 && mp[x][y]>maxValue))
        {
            //记录当前有效行动方案数
            dp[x][y][num][maxValue+1] = 1;
            return 1;
        }
        else
        {
            //记录当前有效行动方案数
            dp[x][y][num][maxValue+1] = 0;
            return 0;
        }
    }
    long long s=0;
    for(int i=0; i<2; i++)
    {
        int newx = x + dx[i];
        int newy = y + dy[i];
        if(newx<=n && newy<=m)
        {
            //如果当前格子中宝物价值大于手中任意一件那么就有两种选择,一种是拿另一种是不拿。
            if(mp[x][y] > maxValue)
            {
                s += dfs(newx,newy,num+1,mp[x][y])%Q;
            }
            s += dfs(newx,newy,num,maxValue)%Q;
        }
    }
    //记录当前状态的有效方案数
    dp[x][y][num][maxValue+1] = s%Q;
    return s%Q;
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            cin>>mp[i][j];
        }
    }
    memset(dp,-1,sizeof(dp));
    cout<<dfs(1,1,0,-1)<<endl;
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值