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;
}