欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > 洛谷P9242 [蓝桥杯 2023 省 B] 接龙数列

洛谷P9242 [蓝桥杯 2023 省 B] 接龙数列

2025/2/24 17:47:57 来源:https://blog.csdn.net/2303_80070412/article/details/145790398  浏览:    关键词:洛谷P9242 [蓝桥杯 2023 省 B] 接龙数列

题目描述

对于一个长度为 K 的整数数列:A1​,A2​,…,AK​,我们称之为接龙数列当且仅当 Ai​ 的首位数字恰好等于 Ai−1​ 的末位数字(2≤i≤K)。

例如 12,23,35,56,61,11 是接龙数列;12,23,34,56 不是接龙数列,因为 56 的首位数字不等于 34 的末位数字。所有长度为 1 的整数数列都是接龙数列。

现在给定一个长度为 N 的数列 A1​,A2​,…,AN​,请你计算最少从中删除多少 个数,可以使剩下的序列是接龙序列?

输入格式

第一行包含一个整数 N。

第二行包含 N 个整数 A1​,A2​,…,AN​。

输出格式

一个整数代表答案。

输入输出样例

输入 

5
11 121 22 12 2023

输出

1

说明/提示

【样例说明】

删除 22,剩余 11,121,12,2023 是接龙数列。

【评测用例规模与约定】

对于 20% 的数据,1≤N≤20。

对于 50% 的数据,1≤N≤104。

对于 100% 的数据,1≤N≤105,1≤Ai​≤109。所有 Ai​ 保证不包含前导 0。

蓝桥杯 2023 省赛 B 组 E 题。
P9242 [蓝桥杯 2023 省 B] 接龙数列 - 洛谷


题目及思路:遇到最值问题我们可以往动态规划上思考,题目说要求最少删除多少个数后能形成最长接龙序列,所以说我们可以转换一下,求出最长接龙序列,然后总数n-最长接龙序列=最少删除个数。就比如样例中,总数n=5,然后求最长接龙序列,求出的最长接龙序列为4,所以5-4=1,即为最少删除个数1后能形成最长接龙序列。那所以我们如何来求最长接龙序列呢?

动规五部曲(dp含义、递推公式、初始化、遍历顺序、打印数组) ,主要的就是把大问题拆解成小问题,小问题的值保存下来,从小问题的答案推导到最终答案,空间换时间。


dp含义: dp[last] 表示以 last 结尾的最长接龙序列长度

一开始我看着有点像最长递增子序列(LIS),都是要找一个满足某种条件的最长子序列。如果按照传统的LIS思路,定义dp[i]表示以第i个数结尾的最长接龙序列长度,那么对于每个i,需要遍历前面所有的j,找到满足条件的j,然后更新dp[i]。这样的话时间复杂度还是O(N²),显然不行。所以必须得想办法优化状态转移的过程。

那为什么要这样定义呢?接龙数列的核心规则是:
当前数的首位必须等于前一个数的末位
这意味着,每个数的作用完全由其 首位(s 和 末位(e 决定,与数值本身无关。
所以利用末位数字只有 0~9 的特点,将状态维度压缩到 10,使得时间复杂度为 O(N)。

我们可以数组dp[last],其中dp[last]表示以数字d结尾的最长接龙序列的长度。比如,dp[3]表示当前所有以3结尾的序列中最长的那个的长度。对于当前处理的数num,它的首位是s,末位是e。那么这个数可以接在所有以s结尾的序列后面,形成一个新的以e结尾的序列,新的长度就是dp[s] + 1。然后我们需要比较这个新的长度是否比当前的last[e]大,如果更大,就更新last[e]。再比如当前num首位为3,末位为7,这时候我们要形成接龙序列,则是看与首位3相同的前面的末尾也是3的数,而在此之前,我们每次记录的都是以数字d结尾的最长接龙序列的长度,与dp[3]相关,dp[3]等于多少则是说明当前数能与前面形成接龙序列长度就为多少再加1,然而末位是7,所以形成的最新接龙序列长度为dp[7] = dp[3] +1。(13,32,23,37)

举个例子,比如输入数列是11,121,22,12,2023。第一个数11的首位和末位都是1,所以last[1]会被更新为1。第二个数121的首位是1,末位是1,所以它可以接在last[1]=1后面,形成长度为2的序列,更新last[1]为2。第三个数22的首位是2,末位是2,此时last[2]初始为0,所以它自己作为一个新序列,长度1,更新last[2]为1。第四个数12的首位是1,末位是2,此时last[1]=2,所以新长度是3,更新last[2]为3。最后一个数2023的首位是2,末位是3,此时last[2]=3,所以新长度是4,更新last[3]为4。最终最长序列长度是4,总长度5,所以需要删除1个数。

所以进一步得出递推公式

对于每个数 num,假设其首位为 s,末位为 e

  • 如果 num 能接在某个以 s 结尾的序列后,则新序列长度为 dp[s] + 1

  • 如果这个新长度比当前记录的 dp[e] 更长,则更新 dp[e]

公式表达:

dp[e]=max⁡(dp[e],dp[s]+1)
 

初始化

  • 初始时,所有 last[d] 均为 0,表示没有任何序列。

  • 当处理第一个数时,其自身形成长度为 1 的序列,更新 last[e] = 1

遍历顺序:从前往后

打印数组:当遇到疑惑或者提交错误时,打印数组出来比较快速的看看哪一步有错。

以下是我c语言代码仅供参考:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<limits.h>
#include<stdlib.h>
#include<math.h>
#include <stdbool.h>// 获取数字的首位
int get_first_digit(int num) 
{while (num >= 10) {num /= 10;}return num;
}int main() 
{int max_len = 0;int n;scanf("%d", &n);// dp[last] 表示以 last 结尾的最长接龙序列长度int dp[10] = { 0 }; // 末位数字只能是 0~9,所以数组大小是10for (int i = 0; i < n; i++){int num;scanf("%d", &num); // 计算当前数字的首位和末位int s = get_first_digit(num);int e = num % 10;dp[e] = dp[e] > dp[s] + 1 ? dp[e] : dp[s] + 1;// 更新最长的接龙序列长度if (dp[e] > max_len) {max_len = dp[e];}}// 最少需要删除的数的个数 = 总长度 - 最长序列长度printf("%d\n", n - max_len);return 0;
}

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词