蓝桥杯试题 历届试题 地宫取宝

蓝桥杯试题 历届试题 地宫取宝

问题描述
  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件
这个题目从三个方面入手

  1. 如果格子中的物品价值比自己物品最大价值大,那么就选取。

  2. 如果格子中的物品价值比自己物品最大价值大,那么就不选取

  3. 如果格子中的物品价值比自己物品最大价值小,那么就不选取

    由于对于2.3都是不选取,所以可以将其归并为一类,即,不选取格子中的物品,则变成两个方面。


  1. 对于格子中的物品价值比自己物品最大价值大,那么就选取
  2. 不选取格子中的物品

接下来再说说他的中止条件

  1. 越界(即横坐标 = m 或者 纵坐标 = n)
  2. 到达目地的(即即横坐标 = m - 1 或者 纵坐标 = n - 1)

那么对于我们到达终点,有两种状态

  1. 我们已经捡了k件物品无法再捡,这种情况成立,方案数加一
  2. 我们捡了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],用于记忆。
那么怎么在以上的程序进行改进呢?

  1. 将void类型改为long long 这样用于记忆的数组可以直接在原有的已知的方法数上进行操作。
  2. 将return ;的地方改为返回值
  3. 对于已经计算出来的值进行储存
  4. 对于已经有的值,可以直接进行运用
  5. 将之前的全局变量sum取消

说一下这个该转化过程中细节的地方

  1. 可能有些答案或者样例无解,所以要将memory全部初始化为-1,避免混淆是初始化还是所得的结果的
  2. 因为我们传入的上面的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));
}

如有错误,麻烦指出!谢谢观看!

发布了13 篇原创文章 · 获赞 5 · 访问量 489

猜你喜欢

转载自blog.csdn.net/qq_44410340/article/details/104971706