Description
给
n
个
数
,
每
两
个
相
同
的
数
x
可
以
合
并
成
一
个
x
+
1
,
问
最
后
合
并
成
的
序
列
最
短
长
度
?
给n个数,每两个相同的数x可以合并成一个x+1,问最后合并成的序列最短长度?
给n个数,每两个相同的数x可以合并成一个x+1,问最后合并成的序列最短长度?
Solution
首
先
,
我
们
明
确
一
点
一
段
序
列
如
果
可
以
合
并
成
一
个
数
,
那
么
这
个
数
字
一
定
是
唯
一
的
故
,
每
段
定
长
的
区
间
[
l
,
r
]
,
一
定
是
由
中
间
一
个
k
,
[
l
,
k
]
和
[
k
+
1
,
r
]
转
化
那
么
,
我
们
先
d
p
[
l
]
[
r
]
表
示
区
间
[
l
,
r
]
合
并
成
的
数
是
多
少
,
不
能
合
并
则
为
0
状
态
转
移
方
程
为
首先,我们明确一点\\ 一段序列如果可以合并成一个数,那么这个数字一定是唯一的\\ 故,每段定长的区间[l,r],一定是由中间一个k,[l,k]和[k+1,r]转化\\ 那么,我们先dp[l][r]表示区间[l,r]合并成的数是多少,不能合并则为0\\ 状态转移方程为
首先,我们明确一点一段序列如果可以合并成一个数,那么这个数字一定是唯一的故,每段定长的区间[l,r],一定是由中间一个k,[l,k]和[k+1,r]转化那么,我们先dp[l][r]表示区间[l,r]合并成的数是多少,不能合并则为0状态转移方程为
d
p
[
l
]
[
r
]
=
d
p
[
l
]
[
k
]
+
d
p
[
k
+
1
]
[
r
]
(
d
p
[
l
]
[
k
]
=
=
d
p
[
k
+
1
]
[
r
]
)
dp[l][r]=dp[l][k]+dp[k+1][r](dp[l][k]==dp[k+1][r])
dp[l][r]=dp[l][k]+dp[k+1][r](dp[l][k]==dp[k+1][r])
那
么
现
在
我
们
求
出
了
哪
些
区
间
是
可
以
合
并
的
,
问
题
就
变
成
了
一
段
区
间
某
个
地
方
可
以
合
并
,
求
最
短
长
度
设
f
[
i
]
为
合
并
前
i
个
数
最
短
长
度
那么现在我们求出了哪些区间是可以合并的,问题就变成了\\ 一段区间某个地方可以合并,求最短长度\\ 设f[i]为合并前i个数最短长度
那么现在我们求出了哪些区间是可以合并的,问题就变成了一段区间某个地方可以合并,求最短长度设f[i]为合并前i个数最短长度
f
[
i
]
=
m
i
n
i
=
l
r
(
f
[
l
−
1
]
+
1
)
(
若
d
p
[
l
]
[
i
]
!
=
0
)
f[i]=min_{i=l}^{r} (f[l-1]+1)(若dp[l][i]!=0)
f[i]=mini=lr(f[l−1]+1)(若dp[l][i]!=0)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#pragma GCC optimize(3, "Ofast", "inline")
#define fir(i, a, b) for (int i = a; i <= b; i++)
#define mov(i) (1ll << i)
#define MAX_INF 0x3f3f3f3f
const int maxn = 5e2 + 10;
int a[maxn];
int dp[maxn][maxn];
int f[maxn];
int main()
{
int n;
cin >> n;
fir(i, 1, n) scanf("%d", &a[i]);
fir(i,1,n)
dp[i][i]=a[i];
for(int len=2;len<=n;len++)
{
for(int l=1,r=len;r<=n;l++,r++)
{
for(int k=l;k<r;k++)
if(dp[l][k]&&dp[l][k]==dp[k+1][r])
dp[l][r]=dp[l][k]+1;
}
}
memset(f,MAX_INF,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++)
{
for(int l=1;l<=i;l++)
{
if(dp[l][i])
f[i]=min(f[i],f[l-1]+1);
}
}
cout<<f[n]<<endl;
}