题目描述
对于一个递归函数w(a,b,c)
- 如果 a≤0 or b≤0 or c≤0 就返回值1.
- 如果 a>20 or b>20 or 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,ca,b,c均为15时,调用的次数将非常的多。你要想个办法才行.
/ absi2011 : 比如 w(30,-1,0)w(30,−1,0)既满足条件1又满足条件2
这种时候我们就按最上面的条件来算
所以答案为1
/
输入输出格式
输入格式:
会有若干行。
并以-1,-1,-1−1,−1,−1结束。
保证输入的数在[−9223372036854775808,9223372036854775807]之间,并且是整数。
输出格式:
输出若干行,每一行格式:
w(a, b, c) = ans
注意空格。
输入输出样例
输入样例#1:
1 1 1
2 2 2
-1 -1 -1
输出样例#1:
w(1, 1, 1) = 2
w(2, 2, 2) = 4
说明
记忆化搜索
题解
这个题目已经告诉我们用记忆化搜索了。。。在此之前我们先看看如何记忆化搜索斐波那契数列。
斐波那契数列满足 f(n) = f(n-1) + f(n-2) 如果当 n 很大时按照平常的递归代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std ;
ll f( ll n ){
if ( n == 0 ){
return 0 ;
}else if ( n == 1 || n == 2 ){
return 1 ;
}
return f(n - 1) + f(n - 2) ;
}
int main(){
ll n ;
while ( cin >> n ) {
cout << f(n) << endl ;
}
}
当 n 到 36 左右时速度已经明显变慢。如果采用记忆化搜索的办法呢?代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using namespace std ;
const int MAXN = 305 ;
ll ans[MAXN] ;
ll f( ll n ){
if ( n == 0 ){
return 0 ;
}else if ( n == 1 || n == 2 ){
return 1 ;
}else if ( ans[n] != 0 ){
return ans[n] ;
}
return ans[n] = f(n - 1) + f(n - 2) ;
}
int main(){
ll n ;
while ( cin >> n ){
memset(ans , 0 , sizeof(ans)) ;
cout << f(n) << endl ;
}
}
当 n 大于 36 甚至到 90 左右(快炸 long long 了..)都能瞬间出答案。所以记忆化搜索的思想就是记录下每一次计算的答案,如果这个值存在的话就直接返回这个值,省去了递归中重复计算的过程,可以省去大量的时间,这或许也是个用空间换时间的一个典型例子。
所以本道题也可以用记忆化搜索的思想去解决,记录中间结果减少重复计算从而省去大量时间。代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using namespace std ;
ll ans[25][25][25] ;
void init(){
for ( int i = 0 ; i < 25 ; i ++ ){
for ( int j = 0 ; j < 25 ; j ++ ){
for ( int k = 0 ; k < 25 ; k ++ ){
ans[i][j][k] = -1 ;
}
}
}
return ;
}
ll w ( ll a , ll b , ll c ){
if ( a <= 0 || b <= 0 || c <= 0 ){
return 1 ;
}else if ( ans[a][b][c] != -1 ){
return ans[a][b][c] ;
}else if ( a > 20 || b > 20 || c > 20 ){
ans[a][b][c] = w(20 , 20 , 20) ;
}else if ( a < b && b < c ){
ans[a][b][c] = w(a , b , c - 1 ) + w(a , b - 1 , c - 1) - w(a , b - 1 , c) ;
}else{
ans[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) ;
}
return ans[a][b][c] ;
}
int main(){
ll a , b , c ;
while ( cin >> a >> b >> c && !(a == -1 && b == -1 && c == -1) ){
init() ;
printf("w(%lld, %lld, %lld) = " , a , b , c) ;
if ( a > 20 ) a = 21 ;
if ( b > 20 ) b = 21 ;
if ( c > 20 ) c = 21 ;
printf("%lld\n" , w(a , b , c)) ;
}
return 0 ;
}