例题 1:乒乓球框
题目描述
两个乒乓球框 A 和 B,A、B 中均只包含红球和黑球。判断 B 中的球是否能被 A "完全包含",即 B 中红球数量≤A 中红球数量,且 B 中黑球数量≤A 中黑球数量。
解题思路
核心是统计 A、B 中红球和黑球的数量并比较。用枚举类型定义 "红球" 和 "黑球",替代传统的 0/1 数字,让代码逻辑更清晰。
代码实现
cpp
运行
cpp
#include <iostream>
#include <string>
using namespace std;
// 用枚举定义球的类型,避免0/1混淆
enum class Ball { Red, Black };
// 统计字符串中红球和黑球的数量
void countBalls(const string& s, int& red, int& black) {
red = 0;
black = 0;
for (char c : s) {
if (c == 'R') {
red++; // 枚举Ball::Red对应'R'
} else if (c == 'B') {
black++; // 枚举Ball::Black对应'B'
}
}
}
int main() {
string A, B;
cin >> A >> B;
int a_red, a_black, b_red, b_black;
countBalls(A, a_red, a_black);
countBalls(B, b_red, b_black);
// 判断B是否被A包含
if (b_red <= a_red && b_black <= a_black) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
return 0;
}
详细解释
- 枚举类型的作用 :用
Ball::Red和Ball::Black明确标记球的类型,比直接用 0 代表红球、1 代表黑球更直观,避免后续逻辑中数字含义混淆。 - 统计逻辑:遍历字符串 A 和 B,通过字符 'R' 和 'B' 分别对应红球和黑球,统计数量后直接比较,满足 "B 的红球数≤A 的红球数且黑球数≤A 的黑球数" 则输出 Yes,否则 No。
例题 2:组队竞赛
题目描述
有 n 个选手,每个选手有一个能力值,要组成 m 支队伍,每支队伍 3 人。团队能力值定义为队伍中第二高的能力值,求所有队伍的团队能力值之和的最大值。
解题思路
要使总和最大,需让每支队伍的 "第二高值" 尽可能大。策略:将所有选手能力值排序后,从后往前每 3 人一组,取中间值累加。用枚举标记每组内的 "第一高""第二高""第三高" 位置,让分组逻辑更清晰。
代码实现
cpp
运行
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 枚举每组内的位置:第三高、第二高、第一高
enum class GroupPos { Third, Second, First };
int main() {
int n, m;
cin >> n >> m;
vector<int> skills(n);
for (int i = 0; i < n; ++i) {
cin >> skills[i];
}
// 排序:从小到大
sort(skills.begin(), skills.end());
long long total = 0;
int index = n - 1; // 从最后一个元素开始
for (int i = 0; i < m; ++i) {
// 每组3人:index(First)、index-1(Second)、index-2(Third)
// 取Second位置的值累加
total += skills[index - static_cast<int>(GroupPos::Second)];
index -= 3; // 移动到下一组的First位置
}
cout << total << endl;
return 0;
}
详细解释
- 排序与贪心策略:将能力值排序后,最大的 m 个值会被作为 "第一高",次大的 m 个值作为 "第二高",这正是我们需要累加的部分。例如 n=6,排序后为 1,2,3,4,5,6,m=2 组:第一组 4,5,6 取 5,第二组 1,2,3 取 2,总和 5+2=7。
- 枚举的作用 :用
GroupPos::Second明确标记要取的是每组中间位置(即第二高),避免直接写index-1导致的逻辑模糊,让代码可读性更强。
例题 3:删除相邻数字最大分数
题目描述
给定一个数组,每次可以删除两个相邻且相等的数字,删除后获得这两个数字的和作为分数。重复此操作,直到无法删除,求最大可能的总分数。
解题思路
这是典型的动态规划问题,用dp[i][j]表示区间[i,j]内删除相邻相等数字能获得的最大分数。枚举区间长度和中间点 k,若arr[k] == arr[j],则可尝试合并区间[i,k-1]和[k+1,j-1]的删除结果,再加上2*arr[k]。用枚举标记 "可删除" 状态,让状态转移逻辑更直观。
代码实现
cpp
运行
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 枚举删除状态:未删除、可删除
enum class DeleteState { Undeleted, Deletable };
int main() {
int n;
cin >> n;
vector<int> arr(n);
for (int i = 0; i < n; ++i) {
cin >> arr[i];
}
// dp[i][j]:区间[i,j]的最大分数
vector<vector<int>> dp(n, vector<int>(n, 0));
// 枚举区间长度,从2开始
for (int len = 2; len <= n; ++len) {
// 枚举区间起点i
for (int i = 0; i <= n - len; ++i) {
int j = i + len - 1; // 区间终点j
dp[i][j] = dp[i][j-1]; // 初始化为不删除j的情况
// 枚举中间点k,寻找与arr[j]相等的元素
for (int k = i; k < j; ++k) {
if (arr[k] == arr[j]) {
// 若k和j相邻,直接删除得2*arr[j]
int current = (k == j-1) ? 2*arr[j] : dp[k+1][j-1] + 2*arr[j];
// 加上左边区间[i,k-1]的分数
if (k > i) {
current += dp[i][k-1];
}
dp[i][j] = max(dp[i][j], current);
}
}
}
cout << dp[0][n-1] << endl;
return 0;
}
详细解释
- 动态规划状态定义 :
dp[i][j]记录从索引 i 到 j 的子数组中,删除相邻相等数字的最大分数。 - 枚举与状态转移 :通过枚举区间长度和中间点 k,判断
arr[k]是否与arr[j]相等。若相等,说明k和j可以作为一对被删除,此时分数为区间[i,k-1]的分数 + 区间[k+1,j-1]的分数 + 2*arr[k]。用枚举DeleteState可辅助标记k和j是否处于 "可删除" 状态,让逻辑更清晰。 - 示例:数组 3,4,2,2,4,最优删除路径为:先删 2,2 得 4 分,数组变为 3,4,4;再删 4,4 得 8 分,数组变为 3,总分数 4+8=12,与 dp 04 计算结果一致。
谢谢