区间DP(记搜)——BZOJ1032/Luogu2145 [JSOI2007]祖码Zuma

来源:互联网 发布:北京大学网络本科 编辑:程序博客网 时间:2024/06/05 14:45

题面:BZOJ1032 Luogu2145
一个很明显的区间DP
首先预处理,把相同颜色的连续珠子全部搞到一个数组段里去
v记录该段的数值,w记录该段的长度
定义状态:f[i][j]表示区间从i段j段全部消掉所需最少珠子数
然后一个最显然最基础的状态转移方程:

f[i][j]=min(f[i][j],f[i][k]+f[k+1][j])

这是区间DP几乎必有的转移方程了吧,k从i到j-1枚举
然后就是边界条件和特殊处理的东西了
如果i=j,那么f[i][j]就是这一段要消掉的珠子数啦,题中有讲原来连三颗以上的珠子还需要一颗来激活,所以如果w>=3,f[i][j]=1,否则f[i][j]=2(加2颗凑到3颗)
然后还有一种情况,如果左端点颜色等于右端点颜色,那么f[i][j]=min(f[i][j],f[i+1][j-1]+(两边个数加起来个数是否大于2,是就不用加,不是就+1)
为什么呢,因为保证左右加起来个数肯定大于等于2
然后就可以DP了
这里给上记忆化搜索的代码,因为我感觉记忆化搜索比循环好理解。。。

#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#include<iostream>#include<cstdlib>#include<queue>#include<string>#include<ctime>#include<climits>using namespace std;int f[501][501];int n,m=0,a[1001];int v[1001],w[1001];inline int dfs(int l,int r){    if(f[l][r])return f[l][r];    if(l==r)return f[l][r]=w[l]>1?1:2;    f[l][r]=1e9;    if(v[l]==v[r]){        if(l+1==r)f[l][r]=1;        else f[l][r]=dfs(l+1,r-1)+(w[l]+w[r]>2?0:1);    }    for(int i=l;i<r;i++)f[l][r]=min(f[l][r],dfs(l,i)+dfs(i+1,r));    return f[l][r];}int main(){    scanf("%d",&n);a[0]=-1;    for(int i=1;i<=n;i++){        scanf("%d",&a[i]);        if(a[i]==a[i-1])w[m]++;        else v[++m]=a[i],w[m]=1;    }    printf("%d",dfs(1,m));    return 0;}
原创粉丝点击