题目描述
对于一个递归函数 w(a,b,c)
- 如果 a <= 0 或 b <= 0 或 c <= 0 就返回值 1。
- 如果 a>20 或 b>20 或 c>20 就返回 w(20,20,20)
- 如果 a<b 并且 b<c 就返回 w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c)。
- 其它的情况就返回 w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1)
这是个简单的递归函数,但实现起来可能会有些问题。当 a,b,c 均为 15 时,调用的次数将非常的多。你要想个办法才行。
注意:例如 w(30,-1,0) 又满足条件1 又满足条件 2,请按照最上面的条件来算,答案为 1。
输入格式
会有若干行。
并以 -1,-1,-1 结束。
输出格式
输出若干行,每一行格式:
`w(a, b, c) = ans`
注意空格。
样例 #1
样例输入 #1
1 1 1
2 2 2
-1 -1 -1
样例输出 #1
w(1, 1, 1) = 2
w(2, 2, 2) = 4
提示
数据规模与约定
保证输入的数在 [-9223372036854775808,9223372036854775807] 之间,并且是整数。
保证不包括 -1, -1, -1 的输入行数 T 满足 1 <= T <=10 ^ 5。
如果用普通的递归的话,肯定是会超时的,这道题我们可以考虑记忆化搜索。
题目中提到:调用的次数将非常的多,你要想个办法才行
题目末尾又提到了记忆化搜索
因此,使用记忆化搜索完成此题已经是很明显的了。
记忆化搜索时一定要先想,记忆化的数组要开多大。对于这个题来说,输入数据在long long范围内,对于每一组a,b,c都使用一个变量来进行记忆化是不现实的。
阅读递归函数内容,我们发现,当a<0||b<0||c<0时,返回值都是1,当a>20||b>20||c>20时,返回值都是w(20,20,20),因此,对于以上的这些数据,我们可以不进行记忆化处理。 到了这里,记忆化的数组范围显而易见了,开[30][30][30]的数组绝对够用了。
先看代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int rbg[25][25][25];
bool vis[25][25][25];
int w(ll a, ll b, ll c)
{
if (a <= 0 || b <= 0 || c <= 0)
return 1;
else if (a > 20 || b > 20 || c > 20)
return w(20, 20, 20);
else if (vis[a][b][c])
return rbg[a][b][c];
else if (a < b && b < c)
rbg[a][b][c] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
else
rbg[a][b][c] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);
vis[a][b][c] = true;
return rbg[a][b][c];
}
int main()
{
ll a, b, c;
memset(vis, false, sizeof(vis));
cin >> a >> b >> c;
while (a != -1 or b != -1 or c != -1)
{
printf("w(%lld, %lld, %lld) = %d\n", a, b, c, w(a, b, c));
scanf("%lld %lld %lld", &a, &b, &c);
}
return 0;
}
下面介绍一下记忆化搜索。
记忆化搜索(Memoization)是一种优化技术,用于避免重复计算,特别是在递归问题中。它通过保存已经计算过的结果,以便在后续的计算中直接使用,从而减少重复的计算。
在我的代码中,我使用了一个名为 rpt
的三维数组来保存已经计算过的结果。这个数组的维度与递归函数的参数对应,即 rpt[a][b][c]
表示在给定参数 a
、b
和 c
下,递归函数 w(a, b, c)
的计算结果。如果在之前的计算中已经计算过这个结果,就直接从 rpt
数组中获取,避免了重复的计算。
在递归函数 w(a, b, c)
中,每次计算结果之后,我都将结果存储在 rpt[a][b][c]
中,以便后续的计算中可以直接使用。这样,当我需要再次计算相同的参数组合时,就可以直接从 rpt
数组中获取结果,而无需重新计算一遍。
记忆化搜索的核心思想是:如果我们在递归过程中遇到了重复的子问题,就可以将这些子问题的计算结果保存下来,避免重复计算,从而提高算法效率。
在我的代码中,这部分实现体现在以下几个地方:
- 在递归函数
w(a, b, c)
内部,首先检查是否已经计算过这个参数组合,如果是则直接返回保存的结果。 - 在每次计算出递归函数的结果后,将结果存储在
rpt[a][b][c]
中,以便下次直接使用。
通过这种方式,我实现了对递归计算结果的保存和复用,从而提高了程序的效率。记忆化搜索在许多递归问题中都是一个有用且有效的优化方法。