一、题目:

二、题解一:朴素动态规划 O ( n 2 ) O(n^2) O(n2) 50 50 50%样例
1、解题思路
1)通过观察题目我们发现:题目意思为从序列中最少删除多少数,可以使得接下来的序列是接龙序列
2)因此我们可以将其转化为求最长的接龙子序列的长度 x x x,用 ( n − x ) (n-x) (n−x)即是答案
3)接着题目就变成了典型的 D P DP DP问题:求解最长接龙子序列
2、动态规划做题步骤
1)重述问题:
2)找到最后一步:
- 选择了某个数 a [ x ] a[x] a[x],作为接龙序列的最后一位
3)去掉最后一步骤,原问题变成了什么?(如果可以就尝试用去掉最后一步的状态推导->最后一步)
- 从 ( 1 − > x − 1 ) (1->x-1) (1−>x−1)中,找最长的接龙子序列
原问题的答案=(1~x-1)中,找最长的接龙子序列的长度+1(且最后一位需要满足条件)
4)考虑边界
3、关键代码解析
int col(int x)
{int temp=0;while(x){temp=x%10;x/=10;}return temp;
}for(int i=1;i<=n;i++){int last=col(a[i]); dp[i]=1; for(int j=1;j<=i-1;j++) {if(a[j]%10==last) dp[i]=max(dp[i],dp[j]+1); }}int res=-1;for(int i=1;i<=n;i++) res=max(res,dp[i]);cout<<n-res<<'\n';
三、题解二:DP优化 O ( n ) O(n) O(n)
通过观察我们发现:最长接龙子序列的最后一位仅仅是(1~10),且它具有后效性
因此我们可以考虑简化dp数组
1、解题思路
1)我们可以令dp[d]
为以d
为结尾的最长接龙子序列的长度
- 比如
dp[1]
表示以1
为结尾的最长接龙子序列的长度
2)现在我们枚举到了a[i]
这个数,它的最高位是x
,最后一位是y

3)此时我们发现,这个值有且只有两种情况
- 找到一个以
x
为结尾的一个序列,将其接上去,这样原本结尾是x
的序列,就变成了结尾是y
的序列(此时dp[y]=dp[x]+1) - 或者不接上去,那么
dp[y]
的值也不会受其影响(也就意味着此时有以y
为结尾的已经更优了)(此时dp[y]不变)
4)因此状态转移方程应该为: d y [ y ] = m a x ( d p [ y ] , d p [ x ] + 1 ) dy[y]=max(dp[y],dp[x]+1) dy[y]=max(dp[y],dp[x]+1)
2、关键代码解析:
- 1# 获取一个数字的最高位与最低位数
- 2#选择接入以
first
为结尾,使得以end
为结尾 + 1 +1 +1 - 3# 不接入,以
end
为结尾的已经优于以fir
为结尾的
for(ll i=1;i<=n;i++){ll first=col_fir(a[i]); ll end=a[i]%10;dp[end]=max(dp[end],dp[first]+1); }ll res=-1;for(ll i=1;i<=9;i++) res=max(res,dp[i]);cout<<n-res<<'\n';
3、完整代码解析:
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
const int N=2e5+7;
ll a[N];
ll dp[17];ll col_fir(ll x)
{ll temp=0;while(x){temp=x%10;x/=10;}return temp;
}void solve()
{ll n;cin>>n;for(ll i=1;i<=n;i++) cin>>a[i];for(ll i=1;i<=n;i++){ll first=col_fir(a[i]); ll end=a[i]%10;dp[end]=max(dp[end],dp[first]+1); }ll res=-1;for(ll i=1;i<=9;i++) res=max(res,dp[i]);cout<<n-res<<'\n';
} int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int _=1;while(_--) solve();return 0;
}