记忆化搜索的学习经历——洛谷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

其它别的情况就返回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时,调用的次数将非常的多。你要想个办法才行.

/*

absi2011 : 比如 w(30,-1,0)既满足条件1又满足条件2

这种时候我们就按最上面的条件来算

所以答案为1

*/

输入输出格式

输入格式:

会有若干行.

并以-1,-1,-1结束.

保证输入的数在-9223372036854775808~9223372036854775807之间

并且是整数

输出格式:

输出若干行

格式:

[b]w(a,_b,c)=_你的输出(_代表空格)[/b]

输入输出样例

输入样例#1:
1 1 1
2 2 2
-1 -1 -1
输出样例#1:
w(1, 1, 1) = 2
w(2, 2, 2) = 4

说明

记忆化搜索

-----------------------------------------------------------------------------分割线-----------------------------------------------------------------------------

好,让我开始吧。

先看题目,看起来很简单诶,直接递归调用不就得了?

正当我开始写时,却发现下面有两行字:

说明

记忆化搜索


等等,记忆化搜索是个什么玩意啊?管他呢,先写完提交上去再说。于是我开开心心地写完了。

提交后,却发现TLE,这是为啥啊?于是我拿起笔算了一下,这个时间复杂度竟然高达a^n,怪不得超时。这时我才开始仔细研究“记忆化搜索”这五个字。

先看看“搜索”,这个肯定所有人都懂啊,就是深度优先搜索的那个搜索啊。那什么叫“记忆化”呢?我想啊想,想到了刚刚提交的超时程序,才发现有很多重复计算的,比如在计算w(1,2,3)和w(2,2,3)时,都需要用到w(1,1,2),如果输入时数满足既需要w(1,2,3),也需要w(2,2,3),那就无异于算了两遍w(1,1,2)

那么解决方法瞬间就浮现在我眼前了:既然可能会用两遍w(1,1,2)。那在第一遍肯定需要先算一遍,第二次要用的时候,直接用第一次的结果,不就可以了么?

那么问题又来了,第二次要怎么用呢?我想到了一个非常简单的方法:直接用一个数组把算出来的结果储存下来,下次要用的时候先判断是否算过w(1,1,2),如果算过,就直接用好了。这难道不就是所谓的“记忆化”吗?!

有了这个思路,代码就很简单了,我写道:

int w(int a,int b,int c){
    if(a<=0||b<=0||c<=0) return 1;

//不解释
    if(s[a][b][c]) return s[a][b][c];

//用s[a][b][c]来储存,如果已经计算过一次,就不用再进行计算了
    else if(a>20||b>20||c>20) s[a][b][c]=w(20,20,20);

//不解释
    else if(a

//把计算结果储存在s数组中
    else s[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 s[a][b][c];

//不解释

}

再提交上去,完美AC,原来传说中的记忆化搜索时这个意思啊!

-----------------------------------------------------------------------------分割线-----------------------------------------------------------------------------

最后附上完整代码:

#include

using namespace std;

int s[21][21][21];

long long int a[1000],b[1000],c[1000];

int a1[1000],b1[1000],c1[1000];

int w(int a,int b,int c){

    if(a<=0||b<=0||c<=0) return 1;

    if(s[a][b][c]) return s[a][b][c];

    else if(a>20||b>20||c>20) s[a][b][c]=w(20,20,20);

    else if(a

    else s[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 s[a][b][c];

}

int main(){

    int result[100];

    int i=0;

    int j=0;

    while(1){

        scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);

        if(a[i]==-1&&b[i]==-1&&c[i]==-1) break;

        if(a[i]>21||b[i]>21||c[i]>21) a1[i]=b1[i]=c1[i]=21;

        else{

            a1[i]=a[i];

            b1[i]=b[i];

            c1[i]=c[i];

        }

        result[i]=w(a1[i],b1[i],c1[i]);

        i++;

    }

    for(j=0;j

        printf("w(%lld, %lld, %lld) = %d\n",a[j],b[j],c[j],result[j]);

    }

    return 0;

}

你可能感兴趣的:(NOIP)