P8612 [蓝桥杯 2014 省 AB] 地宫取宝

题目描述

X 国王有一个地宫宝库。是 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是 件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 件宝贝。

输入格式

输入一行 个整数,用空格分开:,,。

接下来有 行数据,每行有 个整数 代表这个格子上的宝物的价值。

输出格式

要求输出一个整数,表示正好取 个宝贝的行动方案数。该数字可能很大,输出它对 取模的结果。

输入输出样例 #1

输入 #1

2 2 2
1 2
2 1

输出 #1

2

输入输出样例 #2

输入 #2

2 3 2
1 2 3
2 1 5

输出 #2

14

说明/提示

时限 1 秒, 256M。蓝桥杯 2014 年第五届省赛

思考历程

首先题目说我们只能向右或者向下移动,并且如果想拿当前宝物,既要保证比手里所有宝物都大。那么这样来看我们肯定要用DFS,但是这个时候我们就要思考一下,普通DFS真的可以吗?

试想一下,我们简单的走迷宫,方案数就是简单的走迷宫,方案数是,这个数值实在是太超标了,那么我们就要进行优化,那么针对题目特性,我们思考:当我们走到一个固定的格子(x,y),我们需要知道以前我们是怎么过来的吗?很显然,不需要,我们只需要知道,我们现在在哪里,手里有几个宝物,手里宝物最大度价值是多少?有了这四个参数,我们会发现只要这四个参数相同,无论你是怎么走过来的,后续方案数都是一模一样的。所以我们将采用记忆化搜搜~

AC

//
// Created by 23264 on 2026/2/7.
//
#include<bits/stdc++.h>
using namespace std;
 
int n,m,k;
vector<vector<int>> Maze;
vector<vector<vector<vector<int>>>> visited;
int dx[2] = {1,0};
int dy[2] = {0,1};
constexpr int MOD = 1000000007;
 
int dfs(int x,int y,int cnt,int maxx)
{
    // 1. 记忆化检查:如果这个状态算过了,直接返回
    // max_val + 1 是因为 max_val 初始可能是 -1,映射到数组下标 0
    if (visited[x][y][cnt][maxx + 1] != -1)
    {
        return visited[x][y][cnt][maxx + 1];
    }
 
    long long ans = 0;
 
    // 2. 终点判断 (Base Case)
    if (x == n - 1 && y == m - 1)
    {
        // 情况A:手里已经是 k 个,且不拿终点这个(或者终点这个不能拿)
        if (cnt == k)
        {
            ans++;
        }
        // 情况B:手里是 k-1 个,且拿了终点这个正好凑够 k 个
        else if (cnt == k - 1 && Maze[x][y] > maxx)
        {
            ans++;
        }
 
        return visited[x][y][cnt][maxx + 1] = ans % MOD;
    }
 
    // 3. 递归逻辑:分为【取】和【不取】两种情况,然后往右或下走
 
    // 策略一:【不取】当前格子的宝物
    // 状态变化:cnt不变,max_val不变
    for (int i = 0;i < 2;i++)
    {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if (nx < 0 || nx >= n || ny < 0 || ny >= m)
        {
            continue;
        }
 
        ans = (ans + dfs(nx,ny,cnt,maxx)) % MOD;
    }
 
    // 策略二:【取】当前格子的宝物
    // 前提:当前宝物价值 > 手中最大值 且 还没拿满 k 个
    if (Maze[x][y] > maxx && cnt < k)
    {
        for (int i = 0;i < 2;i++)
        {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (nx < 0 || nx >= n || ny < 0 || ny >= m)
            {
                continue;
            }
 
            // 状态变化:cnt + 1,max_val 变为当前格子的价值
            ans = (ans + dfs(nx,ny,cnt + 1,Maze[x][y])) % MOD;
        }
    }
 
    // 记录结果并返回
    return visited[x][y][cnt][maxx + 1] = ans;
}
 
int main()
{
    cin >> n >> m >> k;
    Maze.resize(n,vector<int>(m,0));
    visited.resize(n,vector<vector<vector<int>>>(m,vector<vector<int>>(k + 1,vector<int>(14,-1))));
    for (int i = 0;i < n;i++)
    {
        for (int j = 0;j < m;j++)
        {
            cin >> Maze[i][j];
        }
    }
    cout << dfs(0,0,0,-1) << endl;
}