1. 题目背景与需求分析
2024年6月电子学会C++二级考试中的"找座位"题目,是一道典型的算法与实际应用结合的编程题。这类题目在青少年软件编程等级考试中越来越常见,主要考察以下几个核心能力:
- 基础语法掌握:对数组、循环、条件判断等基础语法的熟练运用
- 逻辑思维能力:将实际问题转化为计算机可执行的逻辑步骤
- 边界条件处理:考虑各种可能的输入情况和极端场景
- 代码优化意识:在保证正确性的前提下追求更高效的解决方案
这道题模拟了现实生活中常见的座位分配场景,比如电影院、教室、会议室等场所的座位安排问题。题目通常会给出一个二维的座位矩阵,其中某些座位已被占用(用特定符号表示),要求编程实现自动寻找可用座位并按照特定规则分配的功能。
提示:在实际考试中,这类题目往往会有明确的输入输出格式要求,考生需要仔细阅读题目说明,确保程序能够正确处理给定的输入格式并生成符合要求的输出。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题建模与数据结构选择
2.1 题目抽象化
首先我们需要将实际问题抽象为计算机可以处理的数据模型。典型的"找座位"问题可以建模为:
- 一个M×N的二维矩阵表示座位图
- 矩阵中的每个元素代表一个座位状态:
- '0'表示空座位
- '1'表示已占用座位
- 需要实现的找座位规则可能包括:
- 找到第一个可用的座位
- 找到最适合的座位(如靠中间、靠前等)
- 按照特定优先级顺序分配座位
2.2 数据结构选择
对于这类问题,最合适的数据结构是二维数组(或C++中的vector<vector
- 直观映射:二维数组天然对应座位排列的行列结构
- 高效访问:可以通过行列索引直接访问任意座位状态
- 内存连续:相比链表等结构,数组在内存中的连续存储有利于缓存命中
在C++中,我们可以这样定义座位矩阵:
cpp复制const int ROWS = 10; // 假设有10排座位
const int COLS = 15; // 每排15个座位
int seats[ROWS][COLS]; // 座位状态矩阵
2.3 输入输出规范
考试题目通常会明确输入输出格式,例如:
输入格式:
- 第一行两个整数M和N,表示座位矩阵的行数和列数
- 接下来M行,每行N个数字(0或1),表示座位状态
- 最后一行一个整数K,表示需要分配的座位数
输出格式:
- 对于每个需要分配的座位,输出其行列坐标(从1开始计数)
- 如果无可用座位,输出"Not enough seats"
3. 基础解法实现
3.1 简单遍历法
最直接的解法是顺序遍历整个矩阵,找到第一个可用的座位。这种方法实现简单,但效率不高,特别是在座位矩阵较大时。
cpp复制void findSeatSimple(int seats[][COLS], int M, int N) {
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] == 0) {
seats[i][j] = 1; // 标记为已占用
cout << i+1 << " " << j+1 << endl;
return;
}
}
}
cout << "Not enough seats" << endl;
}
3.2 按行优先搜索
考虑到实际场景中人们通常偏好前排座位,可以改进为按行优先搜索:
cpp复制void findSeatRowPriority(int seats[][COLS], int M, int N) {
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] == 0) {
seats[i][j] = 1;
cout << i+1 << " " << j+1 << endl;
return;
}
}
}
cout << "Not enough seats" << endl;
}
虽然代码看起来与简单遍历法相同,但实际应用中可以通过调整遍历顺序来实现不同的分配策略。
4. 高级解法与优化
4.1 最佳座位选择算法
在实际应用中,我们往往希望找到"最佳"座位而非第一个可用座位。定义"最佳"可以考虑以下因素:
- 距离屏幕/讲台最近(行号最小)
- 位于中间位置(列号最接近N/2)
- 周围座位占用情况(是否希望独处或群体就坐)
我们可以定义一个评分函数来计算每个座位的"质量":
cpp复制int seatScore(int i, int j, int M, int N) {
// 行号越小越好(前排)
int rowScore = (M - i) * 10;
// 列号越接近中间越好
int colScore = 5 - abs(j - N/2);
return rowScore + colScore;
}
void findBestSeat(int seats[][COLS], int M, int N) {
int best_i = -1, best_j = -1, max_score = -1;
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] == 0) {
int score = seatScore(i, j, M, N);
if (score > max_score) {
max_score = score;
best_i = i;
best_j = j;
}
}
}
}
if (best_i != -1) {
seats[best_i][best_j] = 1;
cout << best_i+1 << " " << best_j+1 << endl;
} else {
cout << "Not enough seats" << endl;
}
}
4.2 多座位分配问题
当需要一次性分配多个相邻座位时,问题会变得更加复杂。例如家庭观影希望坐在一起,这时我们需要找到连续的K个空座位。
cpp复制bool checkConsecutiveSeats(int seats[][COLS], int row, int start_col, int k) {
if (start_col + k > COLS) return false;
for (int j = start_col; j < start_col + k; ++j) {
if (seats[row][j] != 0) return false;
}
return true;
}
void findConsecutiveSeats(int seats[][COLS], int M, int N, int k) {
// 优先找同一排的连续座位
for (int i = 0; i < M; ++i) {
for (int j = 0; j <= N - k; ++j) {
if (checkConsecutiveSeats(seats, i, j, k)) {
// 标记这些座位为已占用
for (int s = 0; s < k; ++s) {
seats[i][j+s] = 1;
cout << i+1 << " " << j+s+1 << endl;
}
return;
}
}
}
cout << "Not enough consecutive seats" << endl;
}
5. 边界条件与异常处理
5.1 输入验证
在实际编程考试中,处理异常输入是得分的关键点之一。我们需要考虑:
- 座位矩阵行列数是否合法(正数且在合理范围内)
- 座位状态是否只有0和1
- 需要分配的座位数是否为正数
cpp复制bool validateInput(int M, int N, int seats[][COLS], int K) {
if (M <= 0 || N <= 0 || K <= 0) return false;
if (M > MAX_ROWS || N > MAX_COLS) return false;
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] != 0 && seats[i][j] != 1) {
return false;
}
}
}
return true;
}
5.2 无可用座位处理
当没有足够可用座位时,程序应该优雅地处理这种情况,而不是崩溃或产生错误输出。
cpp复制int countAvailableSeats(int seats[][COLS], int M, int N) {
int count = 0;
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] == 0) count++;
}
}
return count;
}
void findSeatsWithCheck(int seats[][COLS], int M, int N, int K) {
int available = countAvailableSeats(seats, M, N);
if (available < K) {
cout << "Not enough seats (available: " << available << ")" << endl;
return;
}
// 正常分配座位...
}
6. 性能优化技巧
6.1 提前终止搜索
当只需要找到一个座位时,可以在找到后立即终止搜索,避免不必要的遍历。
cpp复制void findSeatWithEarlyExit(int seats[][COLS], int M, int N) {
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] == 0) {
seats[i][j] = 1;
cout << i+1 << " " << j+1 << endl;
return; // 找到后立即退出
}
}
}
cout << "Not enough seats" << endl;
}
6.2 记忆化搜索
对于频繁查询的场景,可以维护一个可用座位的列表或优先队列,避免每次都要全表扫描。
cpp复制struct Seat {
int row, col;
int score;
bool operator<(const Seat& other) const {
return score < other.score; // 用于优先队列,分数高的优先
}
};
priority_queue<Seat> availableSeats;
void initializeSeatQueue(int seats[][COLS], int M, int N) {
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] == 0) {
int score = seatScore(i, j, M, N);
availableSeats.push({i, j, score});
}
}
}
}
void findBestSeatWithQueue() {
if (!availableSeats.empty()) {
Seat best = availableSeats.top();
availableSeats.pop();
cout << best.row+1 << " " << best.col+1 << endl;
} else {
cout << "Not enough seats" << endl;
}
}
7. 实际考试中的注意事项
7.1 代码风格与注释
在等级考试中,良好的代码风格和适当的注释可以帮助阅卷老师理解你的思路,即使有小的错误也可能获得部分分数。
cpp复制/*
* 函数功能:寻找最佳可用座位
* 参数说明:
* seats - 座位状态矩阵
* M - 行数
* N - 列数
* 返回值:无,直接输出结果
*/
void findBestSeat(int seats[][COLS], int M, int N) {
// 初始化最佳座位信息和最高分
int bestRow = -1, bestCol = -1;
int maxScore = -1;
// 遍历所有座位
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
// 只考虑空座位
if (seats[i][j] == 0) {
int currentScore = seatScore(i, j, M, N);
// 更新最佳座位
if (currentScore > maxScore) {
maxScore = currentScore;
bestRow = i;
bestCol = j;
}
}
}
}
// 输出结果或提示无可用座位
if (bestRow != -1) {
seats[bestRow][bestCol] = 1; // 标记为已占用
cout << bestRow+1 << " " << bestCol+1 << endl;
} else {
cout << "Not enough seats" << endl;
}
}
7.2 测试用例设计
在考试中,设计好的测试用例可以验证程序的正确性。应考虑以下情况:
- 所有座位都空
- 所有座位都被占用
- 随机分布的座位占用情况
- 边界情况(如只有一排或一列)
- 需要分配多个座位的情况
cpp复制void testFindSeat() {
// 测试用例1:所有座位都空
int seats1[2][3] = {{0,0,0},{0,0,0}};
cout << "Test case 1: ";
findBestSeat(seats1, 2, 3); // 应输出1 2
// 测试用例2:所有座位都被占用
int seats2[2][3] = {{1,1,1},{1,1,1}};
cout << "Test case 2: ";
findBestSeat(seats2, 2, 3); // 应输出无足够座位
// 测试用例3:混合情况
int seats3[3][4] = {{1,0,0,1},{0,1,0,0},{1,0,1,0}};
cout << "Test case 3: ";
findBestSeat(seats3, 3, 4); // 应输出1 2或1 3
}
7.3 时间与空间复杂度分析
在考试中,理解算法复杂度可以帮你选择更优的解决方案:
-
简单遍历法:
- 时间复杂度:O(M×N)
- 空间复杂度:O(1)
-
最佳座位选择:
- 时间复杂度:O(M×N)
- 空间复杂度:O(1)
-
使用优先队列:
- 初始化:O(M×N log(M×N))
- 每次查询:O(log k),k为队列大小
- 空间复杂度:O(M×N)
在实际考试中,通常矩阵不会太大,因此简单的O(M×N)解法已经足够。但如果题目特别说明矩阵可能很大,就需要考虑更优化的算法。
8. 扩展思考与实际应用
8.1 更复杂的座位分配策略
在实际应用中,座位分配可能需要考虑更多因素:
- 社交距离:疫情期间需要保持座位间隔
- 特殊需求:为残障人士预留座位
- 团体分配:确保团体成员坐在一起
cpp复制// 考虑社交距离的座位分配
bool isSeatValidWithSocialDistancing(int seats[][COLS], int i, int j, int M, int N) {
if (seats[i][j] != 0) return false;
// 检查周围8个方向
for (int di = -1; di <= 1; ++di) {
for (int dj = -1; dj <= 1; ++dj) {
if (di == 0 && dj == 0) continue;
int ni = i + di, nj = j + dj;
if (ni >= 0 && ni < M && nj >= 0 && nj < N) {
if (seats[ni][nj] == 1) return false;
}
}
}
return true;
}
void findSeatWithSocialDistancing(int seats[][COLS], int M, int N) {
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (isSeatValidWithSocialDistancing(seats, i, j, M, N)) {
seats[i][j] = 1;
cout << i+1 << " " << j+1 << endl;
return;
}
}
}
cout << "No valid seat with social distancing" << endl;
}
8.2 与其他算法的结合
座位分配问题可以与许多经典算法结合:
- 图论:将座位看作图中的节点,邻接关系作为边
- 贪心算法:每次选择当前最优的座位
- 回溯法:尝试各种可能的分配组合
cpp复制// 使用贪心算法分配多个座位
void assignMultipleSeats(int seats[][COLS], int M, int N, int K) {
vector<pair<int, int>> assigned;
while (K > 0) {
int best_i = -1, best_j = -1, max_score = -1;
// 寻找当前最佳座位
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
if (seats[i][j] == 0) {
int score = seatScore(i, j, M, N);
if (score > max_score) {
max_score = score;
best_i = i;
best_j = j;
}
}
}
}
if (best_i == -1) break; // 无更多可用座位
// 分配该座位
seats[best_i][best_j] = 1;
assigned.emplace_back(best_i+1, best_j+1);
K--;
}
// 输出结果
if (assigned.size() < K) {
cout << "Only assigned " << assigned.size() << " seats:" << endl;
}
for (auto& seat : assigned) {
cout << seat.first << " " << seat.second << endl;
}
}
8.3 实际应用场景
这类算法在实际生活中有广泛应用:
- 电影院在线选座系统
- 飞机座位分配
- 教室考试座位安排
- 体育场馆座位管理
- 会议室预约系统
理解这些实际应用场景有助于在考试中更好地把握题目要求,设计出更符合实际需求的解决方案。
