浅谈记忆化搜索

记忆化的本质是: 
先记录,后返回(记住:一定要记录,否则就是普通的递归); 
 查阅记录,如果记录中有,则直接返回。

下面通过几个简单的例子来深入了解一下

1.斐波那契

非记忆化

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;

ll dp[100];
ll F(int x)
{
    if(x==1||x==2)
        return 1ll;

    return F(x-1)+F(x-2);
}
int main()
{
    cout<<F(50)<<endl;

    return 0;
}

记忆化搜索

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;

ll dp[100];
ll F(int x)
{
    if(dp[x])
        return dp[x];
    if(x==1||x==2)
        return 1ll;
        
    dp[x]=F(x-1)+F(x-2);
    return F(x-1)+F(x-2);
}
int main()
{
    cout<<F(50)<<endl;

    return 0;
}

2.求阶乘

非记忆化

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;

int dfs(int n)
{
    if(n==1)return 1;
    return n*dfs(n-1);
}
int main()
{
    int n;
    cin>>n;
    cout<<dfs(n)<<endl;
    return 0;
}

 记忆化

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;

int dp[100];
int dfs(int n)
{
    if(dp[n])return dp[n];
    if(n==1)return 1;
    dp[n]=n*dfs(n-1);
    return n*dfs(n-1);
}
int main()
{
    int n;
    cin>>n;
    cout<<dfs(n)<<endl;
    return 0;
}

3.数的计数 

我们要求找出具有下列性质数的个数,先输入一个自然数n,然后对此自然数按照如下方法进行处理: 
*.不做任何操作 
*.在它左边加上一个自然数,但该自然数不能超过原数的一半; 
*.加上数后,继续按照此规则进行处理,直到不能再次加入自然数为止。 

输入

8

输出

10

分析: 
输入为8,输入的可能性为: 
 

8 
48 
38 
28 
18 
248 
148 
138 
128 
1248

非记忆化搜索

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;

//ll dp[100];
int dfs(int x)
{
    int ans=1;
    for(int i=1;i<=x/2;i++)
    {
        ans+=dfs(i);
    }
    return ans;
}
int main()
{
    int n;
    cin>>n;
    cout<<dfs(n)<<endl;
    return 0;
}

记忆化搜索

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;

int dp[100];
int dfs(int x)
{
    int ans=1;
    if(dp[x])return dp[x];
    for(int i=1;i<=x/2;i++)
    {
        ans+=dfs(i);
    }
    dp[x]=ans;
    return ans;
}
int main()
{
    int n;
    cin>>n;
    cout<<dfs(n)<<endl;
    return 0;
}

4.切棒子

给你一根长n英尺的棒子和一份关于该棒子的价目表如下(其中 i = 1,2,3,…,n),请问如何将这根棒子卖出最高的价格,可以对棒子进行切割。

输入

10
1 5 8 9 10 17 17 20 24 30

输出

30

递推公式为DP(n)=optimal{max{price(i)+DP(n-i)|1≤i≤n}}。

非记忆化

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
int dp[100];
int prime[100];
int dfs(int n)
{
    if(n==0)return 0;
    int ans=-inf;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,dfs(n-i)+prime[i]);
    }
    return ans;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>prime[i];
    printf("%d\n",dfs(n));
    return 0;
}

记忆化

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
int dp[100];
int prime[100];
int dfs(int n)
{
    if(dp[n])return dp[n];
    if(n==0)return 0;
    int ans=-inf;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,dfs(n-i)+prime[i]);
    }
    dp[n]=ans;
    return ans;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>prime[i];
    printf("%d\n",dfs(n));
    return 0;
}

5. 01背包

问题描述: 有N件物品和一个容量为V的背包。(每种物品均只有一件)第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。

DP方法

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
int dp[100];
int w[105];
int v[105];
int main()
{
    int N,V;

    cin>>N>>V;
    for(int i=1;i<=N;i++)cin>>w[i];
    for(int i=1;i<=N;i++)cin>>v[i];

    for(int i=1;i<=N;i++)
        for(int j=V;j>=w[i];j--)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    printf("%d\n",dp[V]);
    return 0;
}

记忆化

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
int dp[105][105];
int w[105];
int v[105];
int dfs(int i,int V)
{
    if(dp[i][V])return dp[i][V];
    if(i==0 || V<=0)return 0;
    if(w[i]>V)dp[i][V]=dfs(i-1,V);
    else dp[i][V]=max(dfs(i-1,V),dfs(i-1,V-w[i])+v[i]);

    return dp[i][V];
}
int main()
{
    int N,V;

    cin>>N>>V;
    for(int i=1; i<=N; i++)cin>>w[i];
    for(int i=1; i<=N; i++)cin>>v[i];

    cout<<dfs(N,V)<<endl;


    return 0;
}

 

 

最后我总结了一个模板(以数的计数为例)

dfs(问题)
{

    if(a已解)//if(dp[x])return dp[x];
    然后:
    查阅记录。
    else//得到问题a的最优解。
    将问题a分成几个子问题(a1,a2,…,ak)
    用dfs(a1)、dfs(a2)、…、dfs(ak)求出问题a的解。 //for(int i=1;i<=x/2;i++){ans+=dfs(i);}
    最后将最优解写入记录。dp[x]=ans;
}

总结:

记忆化搜索实际上就是在普通搜索的基础上加了一个记录(备忘录),每次选择dfs的时候如果发现当前路径以前走过那么就可以省下向下的的过程的、,大大缩减了时间,提高了效率

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值