题目链接
https://codeforces.com/problemset/problem/1132/F
思路
看到数据范围我们很容易想到区间 d p dp dp。
我们设 d p [ i , j ] dp[i,j] dp[i,j]表示删除区间 [ i , j ] [i,j] [i,j]最少需要多少次操作次数,则状态转移方程为:
d p [ i ] [ j ] = m i n ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j] = min(dp[i+1][j],dp[i][j-1]) dp[i][j]=min(dp[i+1][j],dp[i][j−1]), s [ i ] = s [ j ] s[i] = s[j] s[i]=s[j]。
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ k ] + d p [ k + 1 ] [ j ] ) dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]), s [ i ] = s [ j ] s[i]=s[j] s[i]=s[j]。
处理到当前长度的时候,其长度减 1 1 1的状态已经处理好,因此当其两侧的字符 s [ i ] s[i] s[i]与 s [ j ] s[j] s[j]相同时,两侧的字符可以同时消去,并不会影响答案。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e2 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n;
char s[N];
int dp[N][N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
dp[i][j] = inf;
}
}
for (int i = 1; i <= n; i++)
{
dp[i][i] = 1;
}
for (int len = 2; len <= n; len++)
{
for (int i = 1; i + len - 1 <= n; i++)
{
int j = i + len - 1;
if (s[i] == s[j]) dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]);
else
{
for (int k = 1; k < j; k++)
{
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
}
}
}
}
cout << dp[1][n] << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}