【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<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

*/

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