洛谷 P1464 Function

题目描述

对于一个递归函数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 很大时按照平常的递归代码:

#include <iostream>
#include <cstring>
#define ll long long

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 左右时速度已经明显变慢。如果采用记忆化搜索的办法呢?代码如下:

#include <iostream>
#define ll long long

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 了…)都能瞬间出答案。所以记忆化搜索的思想就是记录下每一次计算的答案,如果这个值存在的话就直接返回这个值,省去了递归中重复计算的过程,可以省去大量的时间,这或许也是个用空间换时间的一个典型例子。
所以本道题也可以用记忆化搜索的思想去解决,记录中间结果减少重复计算从而省去大量时间。代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <utility>
#define ll long long

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 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值