蓝桥杯试题 历届试题 地宫取宝
问题描述
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
这个题目第一眼看到就知道用深搜,所以先用深搜试探一下,可能会超时。
这题多了一个限制条件,即所捡的物品价值要比之前捡过的物品的价值都大,并且只能捡k件
这个题目从三个方面入手
-
如果格子中的物品价值比自己物品最大价值大,那么就选取。
-
如果格子中的物品价值比自己物品最大价值大,那么就不选取
-
如果格子中的物品价值比自己物品最大价值小,那么就不选取
由于对于2.3都是不选取,所以可以将其归并为一类,即,不选取格子中的物品,则变成两个方面。
- 对于格子中的物品价值比自己物品最大价值大,那么就选取
- 不选取格子中的物品
接下来再说说他的中止条件
- 越界(即横坐标 = m 或者 纵坐标 = n)
- 到达目地的(即即横坐标 = m - 1 或者 纵坐标 = n - 1)
那么对于我们到达终点,有两种状态
- 我们已经捡了k件物品无法再捡,这种情况成立,方案数加一
- 我们捡了k - 1 件物品,并且在终点(即右下角的结束位置)的物品价值大于所捡最大价值。那么我们也可以将该位置的物品进行捡取,该方案也同时成立,方案数加一。
代码如下
#include <iostream>
using namespace std;
#include <algorithm>
int n, m, k, a[55][55];
long long sum = 0;
#define MAX_SIZE 1000000007
void dfs(int x, int y, int max, int t)//x->横坐标 y->纵坐标 max->捡到的物品最大值, t->捡到的物品的数量
{
if (x == n || y == m)
{
return ;//中止条件1
}
if (x == n - 1 && y == m - 1) //中止条件2
{
if (t == k)//终点状态条件1
{
sum++;
sum %= MAX_SIZE;
}
if (t == k - 1 && a[x][y] > max)//终点状态条件2
{
sum++;
sum %= MAX_SIZE;
}
}
if (a[x][y] > max)
{
dfs(x + 1, y, a[x][y], t + 1);//深搜能够进行的条件1
dfs(x, y + 1, a[x][y], t + 1);
}
dfs(x + 1, y, max, t);//深搜能够进行的条件2
dfs(x, y + 1, max, t);
}
int main()
{
int i, j;
cin >> n >> m >> k;
for (i = 0 ; i < n ; i++)
{
for (j = 0 ; j < m ; j++)
{
cin >> a[i][j];
}
}
dfs(0, 0, -1, 0);//因为输入的价值可能等于0,所以传入-1
cout << sum % MAX_SIZE;;
}
果不其然,深搜超时了······
那么就用一个记忆的数组即memory[x][y][max][t],用于记忆。
那么怎么在以上的程序进行改进呢?
- 将void类型改为long long 这样用于记忆的数组可以直接在原有的已知的方法数上进行操作。
- 将return ;的地方改为返回值
- 对于已经计算出来的值进行储存
- 对于已经有的值,可以直接进行运用
- 将之前的全局变量sum取消
说一下这个该转化过程中细节的地方
- 可能有些答案或者样例无解,所以要将memory全部初始化为-1,避免混淆是初始化还是所得的结果的
- 因为我们传入的上面的max=-1, 所以这个时候我们传到数组里面去,就可能数组下标为-1,就溢出了,但是我们又不得不用-1进行比较,这时候我们可以巧妙的处理,即在数组的max中加一,这样就比较好地处理了。
#include <iostream>
using namespace std;
#include <cstring>
#include <algorithm>
int n, m, k, a[55][55];
#define MAX_SIZE 1000000007
long long memory[55][55][15][15];
long long dfs(int x, int y, int max, int t)//x->横坐标 y->纵坐标 max->捡到的物品最大值, t->捡到的物品的数量
{
long long sum = 0;
if (memory[x][y][max + 1][t] != -1)
{
return memory[x][y][max + 1][t];
}
if (x == n || y == m)
{
return 0;
}
if (x == n - 1 && y == m - 1)
{
if (t == k)
{
sum++;
}
if (t == k - 1 && a[x][y] > max)
{
sum++;
}
sum %= MAX_SIZE;
return sum;
}
if (a[x][y] > max)
{
sum += dfs(x + 1, y, a[x][y], t + 1);
sum += dfs(x, y + 1, a[x][y], t + 1);
}
sum += dfs(x + 1, y, max, t);
sum += dfs(x, y + 1, max, t);
memory[x][y][max + 1][t] = sum % MAX_SIZE;
return sum % MAX_SIZE;
}
int main()
{
int i, j;
cin >> n >> m >> k;
for (i = 0 ; i < n ; i++)
{
for (j = 0 ; j < m ; j++)
{
cin >> a[i][j];
}
}
memset(memory, -1, sizeof(memory));
printf("%lld", dfs(0, 0, -1, 0));
}
如有错误,麻烦指出!谢谢观看!