"ZUMA"(COCI#2009-2010#contest 5)

来源:互联网 发布:美国 博士 知乎 编辑:程序博客网 时间:2024/06/06 00:42

(POJ2915类似)

题意

给定一个序列A长度为N(由1到100的整数构成),再给定一个数M,你可以在序列的最前,最后,或中间任意位置插入任意的数。当你插入数后,如果连续的数达到或超过M,则可以让它们一起消失,两边的数会自动连接在一起。现在要你求出最少需要插入多少个数才能使所有的序列消失。(emmm.....就是小时候玩的祖玛)

分析

首先应该可以想到一个二维的区间dp,f(i,j),表示要使区间(i,j)数消失所需要的最小次数,但是这样还不够,因为我们没法用这个状态表示我们是如何插入数的,所以我们还得在修改一下。

我们在插入数的时候,肯定只会加入与前一个数或后一个数相同的的数,否则加入并不是更优的,所以我们可以得到状态f(i,j,k),表示的是在区间(i,j)前加上一段长度为k,且数字和Ai相同的序列,要消除这整个序列所需要的最小操作次数。

例如对于A={1, 3, 3, 3, 2, 3, 1, 1, 1, 3},f[1][5][3]表示消除序列B={1,1,1, 1, 3, 3, 3, 2}(红色即为加上的序列)所需要的最小次数。

那么对于状态f(i,j,k)可得以下转移:

1.普通情况:

我们可以在当前状态继续在前面加一个与Ai相同的数即:f(i,j,k)=f(i,j,k+1)+1。

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

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

2.当此时k=M-1:

那么我们可以消除前M个啦,且i前进一位,即:f(i,j,k)=f(i+1,j,0)。

注意:此状态与情况1相对

3.最复杂的情况:

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

f(i,j,k)=f(i+1,mid-1,0)+f(mid,j,k+1)-----当Ai=Amid时,枚举即可。

最后附上丑陋的代码:

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;int f[101][101][101];int k,n,d[101];int dp(int l,int r,int addn){if(f[l][r][addn]!=-1) return f[l][r][addn];if(l>r) return 0;if(l==r) return k-addn-1;int res=2147483647;if(addn==k-1) res=min(res,dp(l+1,r,0));if(addn<k-1) res=min(res,dp(l,r,addn+1)+1);for(int j=l+1;j<=r;j++)if(d[l]==d[j])res=min(res,dp(l+1,j-1,0)+dp(j,r,min(k-1,addn+1)));f[l][r][addn]=res;return res;}int main(){freopen("ZUMA.in","r",stdin);freopen("ZUMA.out","w",stdout);memset(f,-1,sizeof(f));scanf("%d %d",&n,&k);for(int i=1;i<=n;i++)scanf("%d",&d[i]);printf("%d",dp(1,n,0));fclose(stdin);fclose(stdout);return 0;}
新人一枚....

请大佬指教


原创粉丝点击