#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=0,M=0,Z=1e9+7,ms63=0x3f3f3f3f; int casenum,casei; int n; int a[505]; int f[505][505]; int dp(int l,int r) { if(l>=r)return 1; if(~f[l][r])return f[l][r]; f[l][r]=dp(l+1,r)+1; for(int i=l+1;i<r;++i)if(a[l]==a[i]) { gmin(f[l][r],dp(l+1,i-1)+dp(i+1,r)); } if(a[l]==a[r])gmin(f[l][r],dp(l+1,r-1)); return f[l][r]; } int main() { while(~scanf("%d",&n)) { MS(f,-1); for(int i=1;i<=n;++i)scanf("%d",&a[i]); printf("%d\n",dp(1,n)); } return 0; } /* 【trick&&吐槽】 这道题很有启发性! 【题意】 给你一个长度为n(500)的数串a[]。数字范围在[1,n]之间。 我们每次可以破坏数串中的任意一个回文子串(破坏之后,两侧的聚拢在一起)。 问你最少的破坏次数,可以破坏掉全部的a[]。 【类型】 DP 【分析】 一开始的设想就是区间DP,然而存在的问题就是,我无法判定破坏子串之后的回文关系。 那要该怎么办才好呢? 一开始就想找完整的回文串,实在是太难了。于是,我们能否把问题简化一些呢? 设想一个DP,f[l][r]表示解决区间[l,r]问题的最少操作数,答案显然就是f[1][n] 我们可以用dp(l,r),以记忆化搜索的形式解决这道题 最基本的问题,if(l==r)f[l][r]=1; 否则,必然满足l<r,这个时候,分析问题,围绕一个位置展开比较好讨论,比如,围绕l展开。 我们的基本操作之一,是把l当做一个长度为1的回文串拿掉,即gmax(f[l][r],1+f[l+1][r]); 我们的基本操作之二,是把l形成的一个长度>1的回文串[l,k]拿掉。 然而这里出现问题了。 first,我们无法快速判定回文串; second,我们可能先取出一个[l,i]内部的串。然后才会形成[l,i]的回文串达到更优效果。 于是,对于基本操作之二,我们需要寻求突破性思维。 其实题目描述中,设计到回文串定义的部分,对我们是一个暗示—— 什么是回文串呢?回文串就是——"最左字符=最右字符,次左字符=次右字符"这样子…… 于是,如果要把l参与组成一个长度>1的回文串,那么,我们首先要匹配一个位置i,使得a[l]==a[i] 我们可以枚举[l+1,r]中的所谓满足a[l]==a[i]的位点i,这是必要条件。 然后,a[l]==a[i],那么,f[l][r]可以对应成两个部分——f[l][i]+f[i+1][r], 也就是说,可以存在一个——gmax(f[l][r],f[l][i]+f[i+1][r])的更新。 那么,对于f[l][i],已经有a[l]==a[i],其值我们要如何计算呢? 嘿,我们可以发现,f[l+1][i-1]内的最后一次消除操作,也必然是以"回文串消除"的形式。 而这个回文串,其左右两边分别套上一个a[l]和a[i],是并不会改变其回文串性质的。 于是,gmax(f[l][r],f[l][i]+f[i+1][r])的更新,实际上也可以写作gmax(f[l][r],f[l+1][i-1]+f[i+1][r]) (l+1是可能大于i-1的,也就是遇到相邻两个相同字符的情形。对于这种情况,我们可以特判处理,也可以初始化"当l>r时,f[l][r]=1") 为什么这个做法是对的呢?因为这个做法,其实枚举遍了所有可能的回文串。 尽管取串是存在顺序的,然而,其顺序对我们现在的这种成本计算方式而言,是没有影响的。 于是,这道题,我们采取这个策略,围绕区间左界l入手,分两种形式讨论,就可以AC掉这道本来无从下手的题目啦。 【时间复杂度&&优化】 O(n^3) 【数据】 4 1 2 3 1 */