原题:https://pintia.cn/problem-sets/994805342720868352/problems/994805437411475456
题目大意
给出M中喜欢的颜色(有先后顺序),然后给出一串长度为L的颜色条,去掉其中不喜欢的颜色,然后求剩下序列的一个子序列,使得这个子序列得颜色顺序符合自己喜欢颜色得先后顺序,不一定所有喜欢得颜色都有,但是要保证长度尽可能地长。
方法1(最长非连续上升子序列问题)
因为喜欢的颜色是不重复的,所以我们可以用一个数组fav[]
存放颜色的喜欢程度,初始化为0,先输入的颜色喜欢程度高,对应的数值越低,从1开始计数,0表示不喜欢。然后再输入颜色条stripe[]
的时候,在线地将颜色转成其喜欢程度,如果不为0,才存储,最后求数组stripe[]
的一个非连续最长上升序列的长度。
/* 1045 Favorite Color Stripe (30 分) */
// 按照最长递增子序列来求解
#include<vector>
#include<iostream>
using namespace std;
int N, M, L; // 最大颜色数,喜爱颜色数,颜色条长度
vector<int> fav; // 每种颜色的喜爱值,越低表示越喜欢
vector<int> stripe; // 颜色条,存储喜爱值就行
int main() {
scanf("%d", &N);
fav.resize(N + 1); // 初始值为0,表示不喜欢
scanf("%d", &M);
int i, j;
int color;
for (i = 1; i <= M; i++) {
scanf("%d", &color);
fav[color] = i;
}
// 输入颜色条,并将其转成喜爱值,喜爱值为0的就丢掉
scanf("%d", &L);
for (j = 1; j <= L; j++) {
scanf("%d", &color);
if (fav[color] >= 1) stripe.push_back(fav[color]);
}
// 找颜色条喜爱值的最长上升子序列的长度
int len = stripe.size();
vector<int> dp(len);
int maxlen = 0; // 最长长度
for (i = 0; i < len; i++) {
dp[i] = 1; // 初始化,自成一个上升子序列
for (j = 0; j < i; j++) {
if (stripe[j] <= stripe[i] && dp[j] + 1 > dp[i]) dp[i] = dp[j] + 1;
// i 接在 j 后面
}
if (dp[i] > maxlen) maxlen = dp[i];
}
printf("%d\n", maxlen);
system("pause");
return 0;
}
方法2(最长公共子序列)
输入喜欢颜色序列fav[]
,和颜色条stripe[]
,然后求其最长非连续公共子序列。
但是,此题fav[]
中的字符可以在stripe[]
中连续出现,普通的LCS的递归式为,若stripe[i] == fav[j]
,则dp[i][j] = dp[i-1][j-1] + 1
,但是由于fav[]
可以重复,则可以先不更新fav
的下标,则有 dp[i][j] = dp[i-1][j] + 1
,等到递归之后不相等再更新。 更一般的,若两个字符串均相互可以重复出现,则递推式改为:若stripe[i] == fav[j]
,则dp[i][j] = max{ dp[i-1][j], dp[i][j-1] } + 1
,即两个字符串均可能延迟改动。
/* 1045 Favorite Color Stripe (30 分) */
// 按照最长公共子串来求解,某一字符串允许重复
#include<vector>
#include<iostream>
using namespace std;
int N, M, L; // 最大颜色数,喜爱颜色数,颜色条长度
vector<int> fav; // 存放喜欢的颜色,长度为M
vector<int> stripe; // 颜色条,长度为L
int dp[10010][10010];
int main() {
scanf("%d %d", &N, &M);
fav.resize(M + 1);
int i, j;
for (i = 1; i <= M; i++) scanf("%d", &fav[i]);
scanf("%d", &L);
stripe.resize(L + 1);
for (i = 1; i <= L; i++) scanf("%d", &stripe[i]);
// 求解stripe(L)和fav(M)的最长公共子序列(非连续)
// 初始化
for (i = 0; i < M; i++) dp[0][i] = 0;
for (i = 0; i < L; i++) dp[i][0] = 0; // 表示一字符串长度为0
int maxlen = 0;
for (i = 1; i <= L; i++) {
for (j = 1; j <= M; j++) {
if (stripe[i] == fav[j]) {
dp[i][j] = dp[i - 1][j] + 1; // j 可以保持不变,这样能够重复
/* 当然,若要保证 双方字符串均可以相互重复出现
* 则应该修改为 dp[i][j] = max{dp[i-1][j],dp[i][j-1]} + 1;
* 只是此题没必要
*/
}
else {
dp[i][j] = dp[i - 1][j] > dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1];
}
}
}
printf("%d", dp[L][M]);
system("pause");
return 0;
}
如果需要回顾其他关于子序列的问题,也可以参考此篇博文子序列问题汇总。