记忆化搜索(Mahmoud and a Message,cf 766C)

学习任何东西都是这样吧。如果长时间没有练习就会生疏和遗忘。

大概唯一的办法就是长时间的反复练习。或者专题训练+打比赛也是不错的。

学而时习之,不会忘乎。。。。。。。。。。

希望自己能越来越会学习。


官方题解是动态规划,我的是记忆化搜索,不过都大同小异了。dp[i]表示后缀i的答案,dx[i]表示后缀i的最少划分。

然后就一路记忆化搜索过去即可,最多有n个状态,时间复杂度为O(n)。

一开始自己以为是数学题,然后不会。想到搜索但觉得搜索量太大会超时。当时觉得搜索的时间复杂度为O(2^n),每个位子都可以分开或者不分开嘛。

我想这大概是因为自己对搜索算法的理解不够深了。

有一种搜索是无返回值,无记录值的搜索,只在找到一个可行解时对全局变量+1,在找不到时就返回。这个方法叫做回溯法,优点是能找到所有可行解及其具体解法,顺便维护了解的个数。但缺点也很明显,那就是时间复杂度高。如果用回溯法对本题进行搜索那么时间复杂度确实是O(2^n)无疑的。但是我们又不需要知道所有的可行解的具体解法,我们甚至连一个可行解的具体解法都不需要知道。我们只需要知道有多少个解就好了难道不是吗?

那么这时候的搜索算法就可以进行改进了,我们的目的不再是搜索出所有解,而是找到解的个数。搜索的目的变了,搜索的很多本质与细节也就变了,我们不再想通过枚举所有解的方式来找到解的个数,而是希望借助某些计算方法,通过搜索的方式把答案计算出来。这又和纯数学题目不同,纯数学题就是带公式计算,而搜索过程中的计算,就有点动态规划或者记忆化搜索的意思了。

充分利用问题的漏洞和规律,通过记录一些值,维护一些值的方式来最大限度降低搜索量,这样的搜索才不叫暴搜。


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll maxn = 1010;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f;

ll n;
char s[maxn];
ll a[30];
ll MAX;
ll dp[maxn];
ll dx[maxn];

ll dfs(ll now)
{
    if(now==n) return 1;
    if(dp[now]) return dp[now];
    ll next=now+1;
    ll l=a[s[next-1]-'a'];
    while(1)
    {
        if(next-now+1>l) break;
        if(next-now+1>a[s[next]-'a']) break;
        l=min(l,a[s[next]-'a']);
        next++;
    }
    MAX=max(MAX,next-now);
    ll ret=0;
    for(ll i=now+1;i<=next;i++)
    {
        ret=(ret+dfs(i))%mod;
        dx[now]=min(dx[now],dx[i]+1);
    }
    return dp[now]=ret;
}

int main()
{
    memset(dx,inf,sizeof(dx));
    scanf("%lld",&n);
    dx[n]=0;
    scanf("%s",s);
    for(ll i=0;i<26;i++)
        scanf("%lld",a+i);
    MAX=1;
    printf("%lld\n",dfs(0));
    printf("%lld\n%lld\n",MAX,dx[0]);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值