[COCI 2010] ZUMA (区间DP+记忆化搜索)

满分做法:

\(dp[i][j][k]\)表示在区间\([i-j]\)加上一段长度为\(k\),且数字和\(a[i]\)相同的序列,要消除这整个序列所需要的最小操作次数。

1.普通情况:我们可以在当前状态继续在前面加一个与\(a[i]\)相同的数即:\(dp[i][j][k]=dp[i][j][k+1]+1\)

(1):"+1"是由于添加一个数就是一次操作。

(2):此时前面一共有k+1个与Ai相同的数。

2.当此时\(k=K-1\):那么我们可以消除前K个啦,且i前进一位,即:\(dp[i][j][k]=dp[i+1][j][0]\)

3.如果在区间\([i,j]\)中在\(a[i]\)后面有和\(a[i]\)相同的的数,根据规则,我们可以先消掉他俩之间的数,再消掉剩下合并的一段,即:

\(dp[l][r][k]=dp[l+1][j-1][0]+dp[j][r][min(k+1,K-1)]\)(a[l]==a[j]),取min方便和方式2结合。

#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int n,k;
int a[110];
int dp[110][110][110];//dp[i][j][k]表示区间i-j加上一段长度为k,且数字和Ai相同的序列,要消除这整个序列所需要的最小操作次数。
int dfs(int l,int r,int add)
{
 if(dp[l][r][add]!=-1) return dp[l][r][add];	
 if(l>r) return 0;
 if(l==r) return k-add-1;//还要补这么多弹
 int res=2147483647;
 if(add==k-1) res=min(res,dfs(l+1,r,0));
 if(add<k-1)  res=min(res,dfs(l,r,add+1)+1);
 for(int j=l+1;j<=r;j++)
 {
  if(a[l]==a[j])
  {
    res=min(res,dfs(l+1,j-1,0)+dfs(j,r,min(add+1,k-1)));//后面取add+1是因为a[l]也算作加上的长度 
  }
 }
 dp[l][r][add]=res;
 return res;
}
int main()
{
 memset(dp,-1,sizeof(dp)); 
 scanf("%d%d",&n,&k);
 for(int i=1;i<=n;i++)
 scanf("%d",&a[i]);
 printf("%d\n",dfs(1,n,0));
 return 0;
}
posted @ 2019-10-17 07:34  lihan123  阅读(264)  评论(0编辑  收藏  举报