题目:
BZOJ1939(权限题)
分析:
这题很容易看出是DP,但是状态和转移都不是很好想……
用
d
p
[
l
]
[
r
]
[
c
]
dp[l][r][c]
dp[l][r][c]表示在
l
l
l前面已经新加了
c
c
c个和
l
l
l一样的弹子时,使区间
[
l
,
r
]
[l,r]
[l,r]消完所需插入的弹子数量
显然,当
c
≥
k
−
1
c\geq k-1
c≥k−1时,这
c
c
c个弹子和
l
l
l组成了连续的至少
k
k
k个弹子,这些情况是类似的(都可以一次消完)。因此可以用
c
=
k
−
1
c=k-1
c=k−1的状态代表所有
c
≥
k
−
1
c\geq k-1
c≥k−1的状态。
对于状态
(
l
,
r
,
k
−
1
)
(l,r,k-1)
(l,r,k−1),
l
l
l可以和前面
k
−
1
k-1
k−1个同色弹子一起消掉,因此只需要考虑要插入多少个才能完全消掉区间
[
l
+
1
,
r
]
[l+1,r]
[l+1,r]。这就得到第一个转移:(因为
[
l
+
1
,
r
]
[l+1,r]
[l+1,r]紧贴着
l
l
l,
l
+
1
l+1
l+1左侧没有新插入的弹子,所以消掉
[
l
+
1
,
r
]
[l+1,r]
[l+1,r]所需插入的弹子数就是
d
p
[
l
+
1
]
[
r
]
[
0
]
dp[l+1][r][0]
dp[l+1][r][0])
d p [ l ] [ r ] [ k − 1 ] = d p [ l + 1 ] [ r ] [ 0 ] dp[l][r][k-1]=dp[l+1][r][0] dp[l][r][k−1]=dp[l+1][r][0]
对于状态
(
l
,
r
,
c
)
(l,r,c)
(l,r,c),在前面插入一个
l
l
l的同色弹子就变成了
(
l
,
r
,
c
+
1
)
(l,r,c+1)
(l,r,c+1),所以比消完
(
l
,
r
,
c
+
1
)
(l,r,c+1)
(l,r,c+1)状态多一步,即:
d
p
[
l
]
[
r
]
[
c
]
=
d
p
[
l
]
[
r
]
[
c
+
1
]
+
1
dp[l][r][c]=dp[l][r][c+1]+1
dp[l][r][c]=dp[l][r][c+1]+1
考虑对于弹子
l
l
l ,除了在它前面加
(
k
−
1
)
(k-1)
(k−1)个同色弹子外,还可以找一个弹子
i
(
i
>
l
,
a
l
=
a
i
)
i(i>l,a_l=a_i)
i(i>l,al=ai),先消去区间
[
l
+
1
,
i
−
1
]
[l+1,i-1]
[l+1,i−1](该区间可能不存在),这样
i
i
i左侧就有
(
c
+
1
)
(c+1)
(c+1)个同色弹子,这就是状态
(
i
,
r
,
c
+
1
)
(i,r,c+1)
(i,r,c+1)。由此得到第三个转移:(注意特判
l
+
1
=
i
l+1=i
l+1=i时状态
(
l
+
1
,
i
−
1
,
0
)
(l+1,i-1,0)
(l+1,i−1,0)不存在,以及
c
+
1
≥
k
c+1\geq k
c+1≥k时取
c
=
k
−
1
c=k-1
c=k−1)
d
p
[
l
]
[
r
]
[
c
]
=
d
p
[
l
+
1
]
[
i
−
1
]
[
0
]
+
d
p
[
i
]
[
r
]
[
c
+
1
]
(
l
+
1
≤
i
−
1
)
dp[l][r][c]=dp[l+1][i-1][0]+dp[i][r][c+1](l+1\leq i-1)
dp[l][r][c]=dp[l+1][i−1][0]+dp[i][r][c+1](l+1≤i−1)
d
p
[
l
]
[
r
]
[
c
]
=
d
p
[
i
]
[
r
]
[
c
+
1
]
(
l
+
1
=
i
)
dp[l][r][c]=dp[i][r][c+1](l+1=i)
dp[l][r][c]=dp[i][r][c+1](l+1=i)
代码:
有了DP方程以后代码还是很好写的
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
namespace zyt
{
const int N = 110, K = 7;
int arr[N], dp[N][N][K];
int work()
{
int n, k;
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> arr[i];
for (int i = 0; i < n; i++)
for (int c = 0; c < k; c++)
dp[i][i][c] = k - c - 1;
for (int len = 2; len <= n; len++)
for (int l = 0; l + len - 1 < n; l++)
{
int r = l + len - 1;
for (int c = k - 1; c >= 0; c--)
{
if (c < k - 1)
dp[l][r][c] = dp[l][r][c + 1] + 1;
else
dp[l][r][c] = dp[l + 1][r][0];
if (arr[l] == arr[l + 1])
dp[l][r][c] = min(dp[l][r][c], dp[l + 1][r][min(k - 1, c + 1)]);
for (int i = l + 2; i <= r; i++)
if (arr[l] == arr[i])
dp[l][r][c] = min(dp[l][r][c], dp[l + 1][i - 1][0] + dp[i][r][min(k - 1, c + 1)]);
}
}
cout << dp[0][n - 1][0];
return 0;
}
}
int main()
{
return zyt::work();
}