【Codeforces Round 336 (Div 2) D】【区间DP 讨论 好题】Zuma 区间取回文串最小操作次数使得取光全串

D. Zuma
time limit per test
2 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Genos recently installed the game Zuma on his phone. In Zuma there exists a line of n gemstones, the i-th of which has color ci. The goal of the game is to destroy all the gemstones in the line as quickly as possible.

In one second, Genos is able to choose exactly one continuous substring of colored gemstones that is a palindrome and remove it from the line. After the substring is removed, the remaining gemstones shift to form a solid line again. What is the minimum number of seconds needed to destroy the entire line?

Let us remind, that the string (or substring) is called palindrome, if it reads same backwards or forward. In our case this means the color of the first gemstone is equal to the color of the last one, the color of the second gemstone is equal to the color of the next to last and so on.

Input

The first line of input contains a single integer n (1 ≤ n ≤ 500) — the number of gemstones.

The second line contains n space-separated integers, the i-th of which is ci (1 ≤ ci ≤ n) — the color of the i-th gemstone in a line.

Output

Print a single integer — the minimum number of seconds needed to destroy the entire line.

Sample test(s)
input
3
1 2 1
output
1
input
3
1 2 3
output
3
input
7
1 4 4 2 3 2 1
output
2
Note

In the first sample, Genos can destroy the entire line in one second.

In the second sample, Genos can only destroy one gemstone at a time, so destroying three gemstones takes three seconds.

In the third sample, to achieve the optimal time of two seconds, destroy palindrome 4 4 first and then destroy palindrome 1 2 3 2 1.


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template inline void gmin(T1 &a,T2 b){if(b=r)return 1;
	if(~f[l][r])return f[l][r];
	f[l][r]=dp(l+1,r)+1;
	for(int i=l+1;i1的回文串[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

*/

你可能感兴趣的:(题库-CF,CodeForces,动态规划-区间DP,讨论,好题)