一、题目:

二、题解一:朴素动态规划
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;
}