题目描述
温温是一只懒惰的小萌猪。他虽然身负重任,但还是每天上班都偷偷玩游戏。今天,他决定玩一个叫做《祖玛》的老游戏。不过,温温打开游戏之后,发现这个游戏和印象里不太一样。
游戏开始时,温温会得到一串宝石,第i个宝石的颜色是ci。游戏的目标是摧毁所有宝石。在游戏的每一秒钟,温温可以选择宝石序列的任意一个回文子串(即连续的若干个宝石,从前往后和从后往前看颜色是一样的),把这些宝石摧毁。之后左右的宝石会向中间移动,重新组成一个连续的序列。
温温想知道他最少要花多长时间才能摧毁所有宝石。
题面翻译和魔改来自hl7.16模拟赛第一题
Input
第一行一个整数n,表示一共有n个宝石。
第二行n个整数ci,表示宝石的颜色。
时间限制:2s
数据范围:
1 ≤ ci ≤ n
1 ≤ n ≤ 20 for 30%
1 ≤ n ≤ 500 for 100%
Output
输出一个整数,表示最少所需时间。
Examples
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
在第三个样例中,温温可以先摧毁回文子串4 4,再摧毁回文子串1 2 3 2 1。
这道题刚看的时候第一反应就是要用马拉车写!(马拉车是求回文串的算法)
因为前几天刚学过(虽说不是一个老师吧)
然后就觉着自己凉了QAQ
于是自暴自弃写了个骗分代码(写了个玄学公式把三个样例过了)
(虽然三道题很毒瘤但是老师还是很贴心的把样例放进去了qwq)
这道题是一道dp题(记忆化搜索就过了)
因为数据范围很小,而且有2s的时间,回文串就直接朴素求(对与一段可能的区间,看两边是否相等)
然后我们看这道题,
对于一个区间[l,r];
区间最优解我们需要分类讨论:
①[l,r]是回文串,那么摧毁[l,r]我们只需要一步
②[l,r]不是回文串,那么[l,r]的摧毁状态就可以用两个小区间更新,那么我们就枚举[l,r]中的两个区间代价和的最小值;
然后我们就不断用小区间去更新大区间,每个区间都会遍历一次,用记忆化搜索或者dp写都行
#include<bits/stdc++.h>
#define N 510
#define INF 0x3f3f3f3f
using namespace std;
int n, m;
int a[N], f[N][N];
int v[N][N];
int dp(int l, int r){
if(l == r) return 1;
if(v[l][r]) return f[l][r];
v[l][r] = 1;
bool flg = 1;
for(int i = l; i <= r; ++i){
if(a[i] != a[r - i + l]){
flg = 0; break;
}
}
if(flg){
f[l][r] = 1;
return 1;
}
int ans = INF;
if(a[l] == a[r]){
ans = min(INF, dp(l + 1, r - 1));
}
for(int i = l; i < r; ++i){
ans = min(ans, dp(l,i) + dp(i + 1, r));
}
f[l][r] = ans;
return ans;
}
int main()
{
// freopen("zuma.in", "r", stdin);
// freopen("zuma.out", "w", stdout);
memset(f, INF, sizeof(f));
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
printf("%d\n", dp(1, n));
return 0;
}