记录一道做过的蓝题
题目描述
假设你有一条长度为 5 的木板,初始时没有涂过任何颜色。你希望把它的 5 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 5 的字符串表示这个目标:\texttt{RGBGR}。
每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成 \texttt{RRRRR},第二次涂成 \texttt{RGGGR},第三次涂成 \texttt{RGBGR},达到目标。
用尽量少的涂色次数达到目标。
输入格式
输入仅一行,包含一个长度为 n 的字符串,即涂色目标。字符串中的每个字符都是一个大写字母,不同的字母代表不同颜色,相同的字母代表相同颜色。
输出格式
仅一行,包含一个数,即最少的涂色次数。
输入输出样例 #1
输入 #1
AAAAA
输出 #1
1
输入输出样例 #2
输入 #2
RGBGR
输出 #2
3
说明/提示
40\% 的数据满足 1\le n\le 10。
100\% 的数据满足 1\le n\le 50。
思路
首先我们可以把涂色过程归类为几种策略:
- 先涂一个大区间,然后一个小区间包含在大区间内。
AAABBBBAAA
- 涂两个并列且没有交集的区间。(如果有交集会互相覆盖,又相当于没有交集了,所以不用考虑)
AAAAABBBB
那么用这两个策略,我们针对一个区间 [l, r]
进行讨论:
- 如果
l = r
,显然有初始化f_l,r = 1
。 - 如果
s_l = s_r
,那么这个区间有可能使用策略一,那么有f_l,r = min{f_l+1,r, f_l,r-1}
。我们想象把第一步的大区间向外延伸一格即可,即可得到当前状态且不用新增步数。 - 无论
s_l
和s_r
是否相等,都有可能使用策略二。我们用区间DP的常规操作,枚举一下断点即可。
有f_l,r = min{f_l,r, f_l,i + f_i+1,r} (l ≤ i < r)
。
时间复杂度 O(n^3)
,空间复杂度 O(n^2)
。
题解代码
#include <bits/stdc++.h>
using namespace std;
int N;
string str;
int dp[301][301];//表示从i到j的最小次数
char s[301];
char tmp;
int main() {
cin>>str;
N = str.size();
memset(dp,0x3f3f,sizeof dp);
for (int i = 1; i <=N; ++i) {
s[i] = str[i-1];
dp[i][i] = 1;
}
for (int len = 2; len <=N ; ++len) {
for (int l = 1; l+len-1<=N; ++l) {
int r = l+len-1;
if(s[l] == s[r])dp[l][r] = min(dp[l+1][r],dp[l][r-1]);
for(int i = l;i < r;i++)
dp[l][r] = min(dp[l][r],dp[l][i]+dp[i+1][r]);
}
}
cout<<dp[1][N];
return 0;
}