1.考虑初始化的方式。html
因为同色转移起来复杂,咱们考虑把相邻的同色的球缩成一个球,记录下缩后的球表明的原来的个数。ios
这时咱们考虑对刷的表的初始化,f[L][R]表示[L,R]区间中须要打入的最小珠子数。git
因为是最小个数答案,因此所有初始化为正无穷,但对于缩后的状态,咱们考虑不受其余合并时影响的结果:spa
若是个数为1,那么此时须要打入两个同色球,f[i][i]=2;code
若是个数大于等于二,那么此时只须要打入一个同色球,f[i][i]=1;htm
即:for(int i=1;i<=list[0];++i)f[i][i]=num[i]>1?1:2;blog
2.考虑DP的方式:常见的枚举断点组合求解。ip
可是咱们注意此题的特殊性质:合并过程当中两端球个数大于等于3直接消掉,因此若是两端颜色相等,咱们枚举断点前先判断:get
若是两端球数合起来多于两个,那么直接等于左右向中间各缩进一个的答案;
若是合起来等于两个,那么还须要再打入一个;
即: if(list[l]==list[r]) f[l][r]=f[l+1][r-1]+(num[l]+num[r]>2?0:1);
剩余部分直接枚举断点松弛大的区间便可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define R register using namespace std; int num[510],list[510],f[510][510]; inline int rd(){ int x=0; bool f=1; char c=getchar(); while(!isdigit(c)){ if(c=='-') f=0; c=getchar(); } while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return f?x:-x; } int init(int n){ int x,cnt=1; for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) f[i][j]=0x3f3f3f3f; list[++list[0]]=rd(); for(int i=2;i<=n;++i){ x=rd(); if(x==list[list[0]])++cnt; else{ num[list[0]]=cnt; list[++list[0]]=x; cnt=1; } } num[list[0]]=cnt; for(int i=1;i<=list[0];++i)f[i][i]=num[i]>1?1:2; } int main(){ init(rd()); for(int len=2;len<=list[0];++len) for(int l=1;l<=list[0]-len+1;++l){ int r=l+len-1; if(list[l]==list[r]) f[l][r]=f[l+1][r-1]+(num[l]+num[r]>2?0:1); for(int k=l;k<r;++k) f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]); } printf("%d",f[1][list[0]]); return 0; }
PS:对于三个离散的点汇集在一块儿的状况,标程是错误的,因此当年数据听说没有那种状况的,至今貌似尚未比较完美的解法,特判就行了......
有关区间DP的其余讲解参考个人随笔:http://www.cnblogs.com/COLIN-LIGHTNING/p/9038198.html