(PAT Advanced)1045.Favorite Color Stripe (最长子序列问题) C++

原题: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;
}

如果需要回顾其他关于子序列的问题,也可以参考此篇博文子序列问题汇总

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值