【蓝桥杯】历届试题 地宫取宝

问题描述

资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
  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 取模的结果。

样例输入1

2 2 2
1 2
2 1

样例输出1

2

样例输入2

2 3 2
1 2 3
2 1 5

样例输出2

14

解题思路

参考思路
1、依照题意,小明每走一步都与之前的举动有关,我们要做的就是一直往下走,走不通了就掉头,换一条路再往下走,可以很自然的想到使用深搜。
2、仅仅使用深度搜索的方法时存在大量重复计算,并且结果超时,因此需要使用数组保存路径值,重复遍历时直接传值。根据题意,每个格子的值与格子横纵坐标,最大价值及获得宝贝数有关,因此用一个四维数组来表示。
3、根据题意,列出其状态转移方程:
在这里插入图片描述
其中i,j表示格子位置,k表示该格子获得了k件宝贝,v表示此时经过路径中,最大值为v。初始情况下k=0,v=-1(v值取值范围为0~12,为了加以区分)。d(i,j,k,v)表示从原点开始达到第(i,j)点,手握k件宝物并且最大价值为v时可以走的路径。
注解:
当小明刚到达(i,j)时会面临两种情况,第一,此格宝物价值高于当前最高价值,小明可以选择捡或不捡;第二,此格宝物价值不高于当前价值,小明不能捡。
同时小明下一步可能走的地点为(i+1,j)或者(i,j+1),因此小明到达下一格时会有6种情况。
反过来说,小明在(i,j)所可能的路径即为这六种情况的路径之和。
4、在遍历的众多过程中,可能存在有一种或多种方式不能得到符合要求的答案,也就是d(i,j,k,v)=0,因此为了区分,初始值设为d=-1。
5、宝物价值初始值设为了-1,为了防止越界,对其下标后移一位。
6、递归至最后一步开始时,符合要求的情况有两种:
第一、此时已有k件宝物;
第二、此时已有k-1件宝物,且最后一个方格的宝物价值大于已拥有的最大值。
PS:该答案可能很大,在取模的同时选择long long更加稳妥。

#include<iostream>
#include<cstring>
using namespace std;
#define MOD %1000000007
int n,m,num;
int map[60][60];
int d[60][60][13][13];
long long dfs(long long i,long long j,long long k,long long v)
{
     long long s=0; 
     if(d[i][j][k][v+1]!=-1)  return d[i][j][k][v+1]; //若该值已发生改变,则不再次计算
     if(i==n&&j==m) //已到达终点 
     {
         if(k==num||(k==num-1&&v<map[i][j])) //如果此时已经拥有K个值或者已经有K-1个值且最后一个值大于已有最大值 ,即存在符合要求的答案 
              d[i][j][k][v+1]=1;
         else
              d[i][j][k][v+1]=0;
         return d[i][j][k][v+1]; 
     }
     else{
         if(v<map[i][j])//若此时最大值小于该点原始值 
         {
              if(i+1<=n)
                  s=(s+dfs(i+1,j,k+1,map[i][j])MOD+dfs(i+1,j,k,v)MOD)MOD;
              if(j+1<=m)
                  s=(s+dfs(i,j+1,k+1,map[i][j])MOD+dfs(i,j+1,k,v)MOD)MOD;
          }
          else {
              if(i+1<=n)
                  s=(s+dfs(i+1,j,k,v)MOD)MOD;
              if(j+1<=m)
                  s=(s+dfs(i,j+1,k,v)MOD)MOD;
          }
          d[i][j][k][v+1]=s;
          return d[i][j][k][v+1];
    } 
}
int main()
{
      cin>>n>>m>>num;
      memset(d,-1,sizeof(d));//初始化所有值为-1 
      for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
              cin>>map[i][j];
      dfs(1,1,0,-1);
      cout<<d[1][1][0][0];
      return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值