八、题目;地宫取宝
问题描述
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
1.分析思路:
有三个条件:
- 入口在左上角,出口在右下角,只能向下或者向右走一个
- 格子中的宝贝价值比手中任意宝贝价值都大,小明可以拿起它,也可以不拿
- 走到出口时,手中的宝贝恰好是k件
刚开始使用深度搜索发现,方案数会变得很复杂
题目的意思很简单,就是用深搜的方式来找出到底有多少路径,但是开始的时候虽然一眼就认定了是深度搜索,但是还真的不知道怎么搜,因为碰到不能捡的物品就不知道该怎么处理了,每一个路口会遇到四种不同的方案,这样下来整道题就组合数方案就很复杂。
后来发现用记忆递归结合深搜可以试一下
2.代码:
#include<iostream>
#include<cstring>
using namespace std;
const int MOD=1000000007;
int n,m,k;
int data[50][50];
long long ans;
long long cache[50][50][14][13];//cache数组中记录的是状态 x、y坐标 拥有宝物数量 拥有宝物的最大值
//如cache[3][4][5][6]=7 即当在map[3][4]且身上有5件宝物 宝物的最大值是6 是到达终点有7中路径
//可以将入口定义为坐标(0,0)
void dfs(int x,int y,int max,int cnt){}
long long dfs2(int x,int y,int max,int cnt){ //当前位置 拥有宝物的数量 拥有的宝物的最大值
//查缓存
if(cache[x][y][max+1][cnt!=-1]) //判断这个状态是否已经走过,如果走过就直接用记录的数值计算
//因为宝物有可能为0所以定义max时用最小值-1 这就导致无法作为下标使用 实际上如果测试数据中宝物价值没有0
//将所有的+1 去掉也是可以的 这里的话如果去掉肯定是有些数据不对的
return cache[x][y][max+1][cnt];
long long ans=0;
if(x==n||y==m||cnt>k) //向右走到n或者向下走到m或者宝物数量达到最大,满足其中一个即可
return 0;
int cur=data[x][y]; //当前坐标位置
if(x==n-1&&y==m-1) //已经面临最后一个格子
{
if(cnt==k||(cnt==k-1&&cur>max)){
ans++;
if(ans>MOD)
ans%=MOD;
}
return ans;
}
if(cur>max){ //可以取这个物品
ans+=dfs2(x,y+1,max,cnt); //向左走
ans+=dfs2(x+1,y,max,cnt); //向右走
}
//对于价值较小,或者价值大,可以不去取这个物品的情况
ans+=dfs2(x,y+1,max,cnt);
ans+=dfs2(x+1,y,max,cnt);
cache[x][y][max+1][cnt]=ans%MOD;
return ans%MOD;
}
int main(){
scanf("%d %d %d",&n,&m,&k);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&data[i][j]);
}
}
dfs(0,0,-1,0); //第一个点的价值可能为0
memset(cache,-1,sizeof(cache));
printf("%lli\n",dfs2(0,0,-1,0));
return 0;
}