题目链接:
题目大意:
给你一个长度为
n
的序列
数据范围:
1≤n≤105
1≤ci≤n
样例:
input
3
1 2 1
output
1
In the first sample , Genos can destroy the entire line in one second.
input
3
1 2 3
output
3
In
the
second
sample
,
Genos
can
only
destroy
one
gemstone
at
a
input
7
1 4 4 2 3 2 1
output
2
In
the
third
sample
,
to
achieve
the
optimal
time
of
two
seconds
,
destroy
palindrome
4
解题思路:
这是一道 区间
状态方程: dp[i][j] 代表完全删除区间 (i,j) 最少需要多少次操作?
转移方程:
- 区间 (i,j) 可以由 区间 (i,k) + 区间 (k+1,j) 转移而来。
- 当 c[i] = c[j] 时,区间 (i,j) 也可以由 区间 (i+1,j−1) 转移而来。(需要注意的是:当 i+1=j 时,直接 dp[i][j] = 1,否则会出现 i+1>j−1 的情况)
小思考1:为什么最外层是枚举 区间长度,而不是直接枚举 区间端点 l,r 呢?
- 答:因为我们转移的时候是从 小区间 转移到 大区间 的。
小思考2: 当 c[i]=c[j] 时,为什么 区间 (i,j) 是直接由 区间 (i+1,j−1) 转移而来,而不是由 区间 (i+1,j−1) + 1 转移而来?
- 答:因为,若完全删除 区间
(i+1,j−1) 最少需要 k 次操作,那么我们可以在前k−1 次操作删掉 区间 (i+1,j−1) 的 k−1 部分,剩下的一部分可以与 c[i],c[j] 形成一个回文串,最后再一起删掉,所以总操作次数还是 k 次。
- 答:因为,若完全删除 区间
最后答案:
dp[1][n] 。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int inf = 1 << 30;
int c[505],dp[505][505];// dp[i][j] : 完全删除区间 (i,j) 最少需要多少次操作?
int main(){
int n;cin >> n;
memset(dp,0x3f,sizeof(dp));// 初始化为正无穷。
for(int i = 1;i <= n;i++) cin >> c[i], dp[i][i] = 1;// 删除一个数时需要一次操作。
for(int len = 2;len <= n;len++){// 枚举区间长度。
for(int l = 1;l <= n - len + 1;l++){//枚举左端点。
int r = l + len - 1;//右端点。
for(int k = l;k < r;k++){
dp[l][r] = min(dp[l][r],dp[l][k] + dp[k + 1][r]);
// 代表区间(l,r) 可以由 (l,k) + (k + 1,r) 转移。
}
if(c[l] == c[r]){//当 a[l] = a[r]时。
if(l + 1 == r) dp[l][r] = 1;//若相邻,则需要一次操作。
dp[l][r] = min(dp[l][r],dp[l + 1][r - 1]);// (l,r) 当然可以由 (l + 1,r - 1) 转移。
}
}
}
cout << dp[1][n] << endl;
}