【洛谷】P8612 [蓝桥杯 2014 省 AB] 地宫取宝 的题解

【洛谷】P8612 [蓝桥杯 2014 省 AB] 地宫取宝 的题解

洛谷传送门

思路

  1. 第一眼审题看到从左上角走到右下角,dfs。

  2. 再继续看题目,有限定条件:

  • 走到一个位置依据已拿宝物的最大价值 m a x n maxn maxn 选择是否拿宝。

  • 只有走到右下角且拿宝数 c n t = k cnt = k cnt=k 的路径才有效。

  1. 所以第一步先把左上到右下的深搜写出来。
  • 因为待会涉及到一个宝物最大价值 m a x n maxn maxn 和拿宝数 c n t cnt cnt 的问题,所以 dfs 传入四个参数。
  1. 但是光这样的条件深搜是得到满分的,所以还得优化。
  • 通过对路径位置之间的关系分析我们可以知道:

  • 在四个参数不变的前提之下,这个状态的 dfs 还是会被计算多次。

  • 从不同方向到达 ( x , y ) (x,y) (x,y) 点,如果之前的拿宝数都是 cnt - 1,而且 m a x n maxn maxn 都要小于 ( x , y ) (x,y) (x,y) 位置的宝物价值 a[x][y],那就会重复计算 (x, y, a[x][y], cnt)。更别说有可能在几步之前取宝数达到 cnt - 1,然后后面几步不拿宝,到 ( x , y ) (x,y) (x,y) 再拿宝到达 c n t cnt cnt 这个数值。

  1. 因此这道题的重复计算是非常恐怖的,我们可以考虑用一个四维数组将已经计算出来的值存下来。
  • 这样就可以在到达一个搜索状态的时候,先去判断这个状态是否已经计算,如果计算过就直接返回之前的结果。

  • 至于为什么是四维数组,这是因为 dfs 的参数有四个,四维分别对应 dfs 的四个参数。

注意点:因为宝物价值是有可能为 0 0 0 的,所以 m a x n maxn maxn 的初值设定成了 − 1 -1 1,但数组是没有负下标的,因此在记忆的时候选择将 m a x n maxn maxn 的下标 + 1 +1 +1,通过偏移量来解决数组越界问题。

代码

#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
int n, m, k;
ll vis[51][51][15][13], a[55][55];
ll dfs(int x, int y, int maxn, int cnt) {//记忆化搜索
    ll res = 0; 
    //到达边界或者取宝数大于k
    if(x > n || y > m || cnt > k) return 0;
    //已经计算过,直接返回
    if(vis[x][y][maxn + 1][cnt] != -1) return vis[x][y][maxn + 1][cnt];
    //边界出口
	if(x == n && y == m) {
    	if(cnt == k || (cnt == k - 1 && a[n][m] > maxn)) return 1;
    	return 0;
    }
	//拿或不拿分别两种走法
    if(a[x][y] > maxn) {
        res += dfs(x + 1, y, a[x][y], cnt + 1);
        res += dfs(x, y + 1, a[x][y], cnt + 1);
	}
    res += dfs(x + 1, y, maxn, cnt);
    res += dfs(x, y + 1, maxn, cnt);
	//结果记忆并返回
    return vis[x][y][maxn + 1][cnt] = res % mod;
}
int main() {
	//freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    memset(vis, -1, sizeof(vis));
    cin >> n >> m >> k;
    for(int i = 1; i <= n; i ++) {
        for(int j = 1; j <= m; j ++) {
            cin >> a[i][j];
        }
    }
    cout << dfs(1, 1, -1, 0);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值