4


Mirko 将 \(N\) 颗弹子排成一排,依次编号为\(1,2,\cdots,n\)\(i\) 号弹子的颜色为\(c_i\) 。他发现,如果他触摸$ ≥K$ 颗连续的弹子,且这些弹子的颜色相同,魔法会使这些弹子消失;此后,这若干颗弹子前面的弹子便与这 若干 颗弹子后面的弹子相邻。

Mirko 家里有很多弹子,他想在这 \(N\)颗弹子之间(也可以在开头的弹子前面或末尾的弹子后面)插入尽可能少的弹子,使得这\(N\) 颗弹子+插入的所有弹子消失。

输入格式
第一行:\(N,k\)。 第二行:\(c_1,c_2,\cdots,c_n\)

输出格式
一行,一个整数,表示他至少要插入几颗弹子。

样例1
input

2 5
1 1

output

3

样例2
input

5 3
2 2 3 2 2

output

2

数据范围
\(1≤N≤100,2≤K≤5,1≤c_i≤100\)

时间限制:2S

空间限制:256MB

是在不知道这题的思路怎么想到的。

定义\(dp_{l,r,x}\)为从\(l\)\(r\)\(l\)的左边有\(x\)个和\(c_l\)一样的,我们最少需要加多少个子弹才可以让他们全部消失。

然后有几种选择。首先如果\(k+1\)大于x,我们可以全部归0。否则我们可以再这里再放一个和\(x\)一样的,\(x+1\)

还有一种情况:如果选择两个相同的数,并且把中间的全部消掉,似乎就可以让两边的合起来,变得更小。我们枚举\(l\)\(r\)中的所有数,如果有与\(c_l\)相同的\(c_i\),那就递归\(l+1,i-1,0\)\(i,r,k+1\).

这样在第三种情况中可能会出现超过\(m\)的情况,为了避免,由于超过了m-1,其实就与m-1等价了。所以跳的时候与m-1去min。

大致就是这样了。虽然是dp,但还是记忆化搜索写法比较容易。

#include
#include
const int N=105;
int n,m,a[N],dp[N][N][6];
int min(int x,int y)
{
	return xr)
		return 0;
	if(l==r)
		return m-k-1;
	if(dp[l][r][k]!=-1)
		return dp[l][r][k];
	int ret=2147483647;
	if(k+1>=m)
		ret=dfs(l+1,r,0);
	else
		ret=dfs(l,r,k+1)+1;
	for(int i=l+1;i<=r;i++)
		if(a[i]==a[l])
			ret=min(ret,dfs(l+1,i-1,0)+dfs(i,r,min(m-1,k+1)));
	return dp[l][r][k]=ret;
}
int main()
{
	memset(dp,-1,sizeof(dp));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	printf("%d",dfs(1,n,0));
}