十四届蓝桥杯CB--接龙数列

本文介绍了动态规划的基本概念、适用问题类型以及如何分析动态规划问题,以接龙数列问题为例,逐步优化求解过程。作者通过实际代码展示了动态规划的应用和代码优化策略,最终得出简洁高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

太久没碰动态规划了,完全忘记了...

在网上学了一下,写出了超时的DP....

最后决定投奔y总orz

废话不多说,上分析

动态规划简介

1.什么是动态规划?

——通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法

说简单点,就是把复杂问题,拆分成子问题,再求解子问题时,记住答案,减少重复计算

2.什么问题可以用动态规划?

——求最值的问题,如最长上升子序列、最小编辑距离、背包问题、凑零钱问题等

如何分析动态规划问题

看了这么多讲解dp的博文,还得是y总的 思想,简单清晰易懂

按照y总的分析法:

DP :

(1)状态表示

        <1>集合:f[i]:以i结尾的所有接龙子序列的集合

        <2>属性:子序列长度的最大值

(2)状态计算

        这个集合中的子序列都是以i结尾的,看倒数第二个数,可以为:无、a1、a2....ai - 1

        求集合的max,只须求每个子集合的值,取max即可

        当取无时,f[i] = 1(题意说了,当只有一个数时,长度为1)

        分析一般情况:倒数第二位数为aj,即......aj ai 那么此时,只须分析.......aj的max,......aj的定义是所有以aj 结尾的接龙子序列的集合,即f[j]

        得到 f[i] = max(f[j] + 1)

本题的分析

本题求的是最少删除多少数 == 找到一个最长子序列使得它是接龙序列(求最值——DP!)

与最长上升子序列类似:每个数能否放在一个数的前面只与它前一个数有关(>前一个数即可)

接龙序列:每个数能否选,只取决于该数的第一位数与前一个数的最后一位数是否相等

DP求解过程见上

代码

#include<iostream>
#include<cstring>
using namespace std;

const int N = 100010;
int f[N],l[N],r[N],n;

int main()
{
    scanf("%d",&n);
    char c[20];  //只须求首位和末位数字,用字符串存
    for(int i = 0;i < n;i ++)
    {
        scanf("%s",c);
        l[i] = c[0] - '0',r[i] = c[strlen(c) - 1] - '0';
    }
    int res = 0;
    for(int i = 0;i < n;i ++)
    {
        f[i] = 1; //无
        for(int j = 0;j < i;j ++) //子集合
        {
            if(r[j] == l[i])
            {
                f[i] = max(f[i],f[j] + 1);
            }
        }
        res = max(res,f[i]); //记录最长的子序列
    }
    printf("%d",n - res);
    return 0;
}

优化

这么交上去,当然会TLE

时间复杂度O(N^2)

仔细分析:在循环j时,其实只需要关心最后一位数=i首位的数

优化方法:开一个数组g[0~9]:g[x]以x为最后一位数的接龙子序列的最大值

优化结果:删除一层for,f[i]直接与g[l[i]]+1比较取max

#include<iostream>
#include<cstring>
using namespace std;

const int N = 100010;
int f[N],l[N],r[N],g[10],n;

int main()
{
    scanf("%d",&n);
    char c[20];
    for(int i = 0;i < n;i ++)
    {
        scanf("%s",c);
        l[i] = c[0] - '0',r[i] = c[strlen(c) - 1] - '0';
    }
    int res = 0;
    for(int i = 0;i < n;i ++)
    {
        f[i] = 1;
        f[i] = max(f[i],g[l[i]] + 1);
        g[r[i]] = max(g[r[i]],f[i]);  //计算出了f[i],记得更新g[]
        res = max(res,f[i]);
    }
    printf("%d",n - res);
    return 0;
}

至此,已经可以ac了,but!真给y总跪了orzzz,还能优化代码

1.因为每次都只用到f[i],i前面的数都没有用,so不用开f[],直接定义一个变量

2.每次也只用一次l[i],r[i],so直接定义一个l、r即可,边输入,边计算

优化代码如下:

#include<iostream>
#include<cstring>
using namespace std;

const int N = 100010;
int g[10],n;

int main()
{
    scanf("%d",&n);
    char c[20];
    int res = 0;
    int f,l,r;
    for(int i = 0;i < n;i ++)
    {
        scanf("%s",c);
        l = c[0] - '0',r = c[strlen(c) - 1] - '0';
        f = 1;
        f = max(f,g[l] + 1);
        g[r] = max(g[r],f);
        res = max(res,f);
    }
    printf("%d",n - res);
    return 0;
}

如此简短优雅的代码,真的让我以为蓝桥杯sooo easy.......

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值