【C++动态规划 离散化】1626. 无矛盾的最佳球队|2027

本文涉及知识点

C++动态规划 离散化

LeetCode1626. 无矛盾的最佳球队

假设你是球队的经理。对于即将到来的锦标赛,你想组合一支总体得分最高的球队。球队的得分是球队中所有球员的分数 总和 。

然而,球队中的矛盾会限制球员的发挥,所以必须选出一支 没有矛盾 的球队。如果一名年龄较小球员的分数 严格大于 一名年龄较大的球员,则存在矛盾。同龄球员之间不会发生矛盾。

给你两个列表 scores 和 ages,其中每组 scoresi 和 agesi 表示第 i 名球员的分数和年龄。请你返回 所有可能的无矛盾球队中得分最高那支的分数 。

示例 1:

输入:scores = 1,3,5,10,15, ages = 1,2,3,4,5

输出:34

解释:你可以选中所有球员。

示例 2:

输入:scores = 4,5,6,5, ages = 2,1,2,1

输出:16

解释:最佳的选择是后 3 名球员。注意,你可以选中多个同龄球员。

示例 3:

输入:scores = 1,2,3,5, ages = 8,9,10,1

输出:6

解释:最佳的选择是前 3 名球员。

提示:

1 <= scores.length, ages.length <= 1000

scores.length == ages.length

1 <= scoresi <= 106

1 <= agesi <= 1000

动态规划+离散化

将分数离散化,并用nums记录原始分数。分数最小的位1,次小的为2 ⋯ \cdots ⋯ 。

将年龄离散化,方便处理比当前队员年龄小的队员。

动态规划的状态表示

dpij 表示 球员的年龄 <= i,离散化后的能力 <=j 组成的无矛盾球队的最大得分。空间复杂度:O(nm) m是离散后的最大年龄

动态规划的填表顺序

将任意最优解,按如下方式排序后,仍然是最优解。

一,年龄小的在前,年龄大的在后。

二,年龄相等,能力小的在前,能力大的在后。

三,年龄能力都相等,按scores中的顺序。

indexs的球员的下标按上述方式排序,按k:indexs顺序处理各球员。

动态规划的转移方程

i = agesk j = scoresk
d p i j = M a x { d p i j + 当前球员的能力 前一个球员年龄相同 d p i − 1 j + 当前球员的能力 前一个球员年龄不同 dpij=Max\begin{cases} dpij+当前球员的能力 &&前一个球员年龄相同 \\ dpi-1j+当前球员的能力 && 前一个球员年龄不同\\ \end{cases} dpij=Max{dpij+当前球员的能力dpi−1j+当前球员的能力前一个球员年龄相同前一个球员年龄不同

每次更新dp后,都要:

cpp 复制代码
for (j =1 ; j <= N; j++) {
						dp[i][j] = max(dp[i][j], dp[i][j - 1]);
						dp[i][j] = max(dp[i][j], dp[i-1][j ]);
					}

时间复杂度:O(nm)

动态规划的初始值

全为0

动态规划的返回值

dp的最大值。

代码

核心代码

cpp 复制代码
class Solution {
		public:
			int bestTeamScore(vector<int>& scores, vector<int>& ages) {
				const int N = scores.size();
				{
					auto tmp = ages;
					tmp.emplace_back(0);
					CDiscretize dis(tmp);
					for (auto& i : ages) {
						i = dis[i];
					}
				}
				const int M = *max_element(ages.begin(), ages.end());
				auto tmp = scores;
				tmp.emplace_back(0);
				CDiscretize dis(tmp);
				for (auto& i : scores) {
					i = dis[i];
				}
				
				vector<int> index(N);
				iota(index.begin(), index.end(), 0);
				sort(index.begin(), index.end(), [&](int i1, int i2) {
					return (ages[i1] < ages[i2]) || ((ages[i1] == ages[i2]) && (scores[i1] < scores[i2]));	});
				vector<vector<int>> dp(M + 1, vector<int>(N + 1));
				int ans = 0;
				for (int k : index) {
					const int i = ages[k];
					int j = scores[k];
					dp[i][j] = max(dp[i - 1][j], dp[i][j]) + dis.m_nums[j];
					ans = max(ans, dp[i][j]);
					for (j =1 ; j <= N; j++) {
						dp[i][j] = max(dp[i][j], dp[i][j - 1]);
						dp[i][j] = max(dp[i][j], dp[i-1][j ]);
					}
				}
				return ans;
			}
		};

单元测试

cpp 复制代码
vector<int> scores, ages;
		TEST_METHOD(TestMethod1)
		{
			scores = { 3,2,4 }, ages = { 1,2,3 };
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(7, res);
		}
		TEST_METHOD(TestMethod11)
		{
			scores = { 1,3,5,10,15 }, ages = { 1,2,3,4,5 };
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(34, res);
		}
		TEST_METHOD(TestMethod12)
		{
			scores = { 4,5,6,5 }, ages = { 2,1,2,1 };
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(16, res);
		}
		TEST_METHOD(TestMethod13)
		{
			scores = { 1,2,3,5 }, ages = { 8,9,10,1 };
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(6, res);
		}
		TEST_METHOD(TestMethod14)
		{
			scores = { 1,1,1 }, ages = { 3,2,1 };
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(3, res);
		}
		TEST_METHOD(TestMethod15)
		{
			scores = { 1,1,1 }, ages = { 1,3,5};
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(3, res);
		}
		TEST_METHOD(TestMethod16)
		{
			scores = { 1,1,1,1,1,1,1,1,1,1 }, ages = { 811,364,124,873,790,656,581,446,885,134 };
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(10, res);
		}
		TEST_METHOD(TestMethod17)
		{
			scores = { 6,5,1,7,6,5,5,4,10,4 }, ages = { 3,2,5,3,2,1,4,4,5,1 };
			auto res = Solution().bestTeamScore(scores, ages);
			AssertEx(43, res);
		}

优化

i1 < i2 ,k1 = indexi1 ,k2 = indexi2。

某方案以球员k1结尾。

如果k1的能力小于等于k2,则此方案加上i2一定不会冲突。

如果 k1的能了大于k2,由于已经按本题解排序,所以k1的年龄一定小于k2:故一定冲突。

故只需枚举能力小于等于当前能力的球员。空间复杂度 :O(n)

可以用线段树。时间复杂度 :O(nlogn)

直接枚举时间复杂度:O(nn)

代码

cpp 复制代码
class CDiscretize //离散化
{
public:
	CDiscretize(vector<int> nums)
	{
		sort(nums.begin(), nums.end());
		nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
		m_nums = nums;
		for (int i = 0; i < nums.size(); i++)
		{
			m_mValueToIndex[nums[i]] = i;
		}
	}
	int operator[](const int value)const
	{
		auto it = m_mValueToIndex.find(value);
		if (m_mValueToIndex.end() == it)
		{
			return -1;
		}
		return it->second;
	}
	int size()const
	{
		return m_mValueToIndex.size();
	}
	vector<int> m_nums;
protected:	
	unordered_map<int, int> m_mValueToIndex;
};

class Solution {
		public:
			int bestTeamScore(vector<int>& scores, vector<int>& ages) {
				const int N = scores.size();			
				CDiscretize dis(scores);
				for (auto& i : scores) {
					i = dis[i];
				}
				vector<int> index(N);
				iota(index.begin(), index.end(), 0);
				sort(index.begin(), index.end(), [&](int i1, int i2) {
					return (ages[i1] < ages[i2]) || ((ages[i1] == ages[i2]) && (scores[i1] < scores[i2]));	});
				 vector<int> dp(N + 1);
				int ans = 0;
				for (int k : index) {
					const int i = ages[k];
					int j = scores[k];
					const int iMax = *max_element(dp.begin() , dp.begin() + j + 1);
					dp[j] = max(dp[j], iMax + dis.m_nums[j]);
					ans = max(ans, dp[j]);
				}
				return ans;
			}
		};

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关推荐
圣保罗的大教堂8 分钟前
leetcode 3756. 连接非零数字并乘以其数字和 II 中等
leetcode
沫璃染墨13 分钟前
《从零入门Linux系统篇(五十七):线程篇·十——生产者消费者模型进阶:从环形缓冲区到POSIX信号量》
linux·运维·服务器·开发语言·c++·系统架构·信号处理
wuminyu24 分钟前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
知识分享小能手1 小时前
C++ 学习教程,从入门到精通,string类和标准模板库 — 完整知识点详解(16)
开发语言·c++·学习
无忧.芙桃1 小时前
位图妙用:512MB 实现 40 亿整数的存在性查询
c语言·c++·面试·哈希算法
ShineWinsu1 小时前
对于Redis:Steam、Geospatial、Hyperloglog、Bitmap、Bitfield类型的解析
数据库·c++·redis·分布式·缓存·面试·zset
朝朝辞暮i2 小时前
C++ 第 31C 课:Subscriber 到底是什么
开发语言·c++·算法
殷色玫瑰2 小时前
C++ string类详解:常用接口、字符串操作与模拟实现
java·linux·c语言·开发语言·数据结构·c++
李游Leo2 小时前
HarmonyOS 7 + Spatial Recon Kit-C++:3DGS 高斯参数的非有限值隔离与可渲染性门禁【鸿蒙心迹】
开发语言·c++·3d·harmonyos
Evand J2 小时前
【MATLAB例程】三维RRT+APF路径规划与AOA-TDOA融合定位算法。附代码的下载链接
算法·matlab·路径规划·rrt·tdoa·apf·aoa