记录一道做过的蓝题

image.png

题目描述

假设你有一条长度为 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

思路

首先我们可以把涂色过程归类为几种策略:

  1. 先涂一个大区间,然后一个小区间包含在大区间内。
    AAABBBBAAA
    
  2. 涂两个并列且没有交集的区间。(如果有交集会互相覆盖,又相当于没有交集了,所以不用考虑)
    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_ls_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;
}

我要成为大剑天尊!