1 条题解
-
0
Thieves and Prisons 题解
问题分析
我们需要为每个抓捕事件分配一个监狱,使得整个事件序列合法。关键约束:
- 抓捕事件:小偷被关进某个监狱
- 开门事件:小偷必须自由,且目标监狱非空
- 监狱可以容纳任意数量小偷
核心思路
关键观察:
- 每个开门事件必须对应之前的一个抓捕事件(被释放的小偷)
- 我们可以将事件序列看作括号匹配问题:
- 抓捕事件相当于左括号
- 开门事件相当于右括号
- 需要为每个"括号对"分配相同的监狱
算法步骤:
- 验证可行性:检查每个开门事件是否合法
- 构建依赖关系:找到每个开门事件对应的抓捕事件
- 分配监狱:为相关的抓捕-开门对分配相同监狱
完整代码实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 100005 typedef struct Event { char type; int thief; int prison; // 输出结果 int match; // 匹配的事件索引 } Event; Event events[MAXN]; int stack[MAXN]; // 用于匹配的栈 int top = -1; int thief_state[MAXN]; // 0:自由, >0:在监狱中 int prison_count[MAXN]; // 每个监狱的小偷数量 // 检查序列是否可行 int check_feasibility(int n, int k, int m) { memset(thief_state, 0, sizeof(thief_state)); for (int i = 0; i < m; i++) { if (events[i].type == 'C') { // 抓捕事件:小偷必须自由 if (thief_state[events[i].thief] != 0) { return 0; } thief_state[events[i].thief] = 1; // 标记为在狱中 } else { // 'O' // 开门事件:小偷必须自由,且目标监狱必须非空 if (thief_state[events[i].thief] != 0) { return 0; } // 这里我们暂时无法检查监狱是否非空,需要在分配时检查 } } return 1; } // 构建事件匹配关系 int build_matching(int m) { top = -1; for (int i = 0; i < m; i++) { if (events[i].type == 'C') { // 抓捕事件压栈 stack[++top] = i; } else { // 'O' // 开门事件:需要找到匹配的抓捕事件 if (top < 0) { return 0; // 没有匹配的抓捕事件 } int capture_idx = stack[top--]; events[capture_idx].match = i; events[i].match = capture_idx; } } // 栈中应该没有剩余元素 return (top == -1); } // 分配监狱 int assign_prisons(int n, int k, int m) { memset(thief_state, 0, sizeof(thief_state)); memset(prison_count, 0, sizeof(prison_count)); // 使用贪心策略分配监狱 int next_prison = 1; for (int i = 0; i < m; i++) { if (events[i].type == 'C') { // 为抓捕事件分配监狱 events[i].prison = next_prison; thief_state[events[i].thief] = next_prison; prison_count[next_prison]++; // 循环使用监狱 next_prison++; if (next_prison > k) next_prison = 1; } else { // 'O' // 开门事件:使用匹配的抓捕事件的监狱 int capture_idx = events[i].match; int prison = events[capture_idx].prison; // 检查监狱是否非空 if (prison_count[prison] == 0) { return 0; } // 释放该监狱的所有小偷 for (int j = 1; j <= n; j++) { if (thief_state[j] == prison) { thief_state[j] = 0; } } prison_count[prison] = 0; } } return 1; } int main() { int n, k, m; scanf("%d %d %d", &n, &k, &m); // 读取事件 for (int i = 0; i < m; i++) { char type[2]; int thief; scanf("%s %d", type, &thief); events[i].type = type[0]; events[i].thief = thief; events[i].match = -1; events[i].prison = 0; } // 步骤1:检查基本可行性 if (!check_feasibility(n, k, m)) { printf("IMPOSSIBLE\n"); return 0; } // 步骤2:构建事件匹配 if (!build_matching(m)) { printf("IMPOSSIBLE\n"); return 0; } // 步骤3:分配监狱 if (!assign_prisons(n, k, m)) { printf("IMPOSSIBLE\n"); return 0; } // 输出结果 for (int i = 0; i < m; i++) { if (events[i].type == 'C') { printf("%d", events[i].prison); } else { printf("%d", events[events[i].match].prison); } if (i < m - 1) printf(" "); } printf("\n"); return 0; }优化版本:更稳健的监狱分配
上面的简单贪心策略可能在某些情况下失败,下面是更稳健的版本:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 100005 typedef struct Event { char type; int thief; int prison; int match; int depth; // 嵌套深度 } Event; Event events[MAXN]; int stack[MAXN]; int top = -1; int thief_state[MAXN]; int prison_available[MAXN]; // 监狱可用性 // 构建匹配关系并计算深度 int build_matching_with_depth(int m) { top = -1; int max_depth = 0; for (int i = 0; i < m; i++) { if (events[i].type == 'C') { // 计算当前深度 events[i].depth = top + 1; if (events[i].depth > max_depth) { max_depth = events[i].depth; } stack[++top] = i; } else { // 'O' if (top < 0) return 0; int capture_idx = stack[top--]; events[capture_idx].match = i; events[i].match = capture_idx; events[i].depth = events[capture_idx].depth; } } return (top == -1) ? max_depth + 1 : 0; } // 基于深度的监狱分配 int assign_prisons_by_depth(int n, int k, int m, int max_depth) { if (max_depth > k) { return 0; // 深度超过监狱数量,不可能 } memset(thief_state, 0, sizeof(thief_state)); memset(prison_available, 1, sizeof(prison_available)); // 为每个深度分配一个监狱 int depth_to_prison[MAXN] = {0}; for (int i = 0; i < max_depth; i++) { depth_to_prison[i] = i + 1; } for (int i = 0; i < m; i++) { if (events[i].type == 'C') { int prison = depth_to_prison[events[i].depth]; events[i].prison = prison; thief_state[events[i].thief] = prison; } else { // 'O' int capture_idx = events[i].match; events[i].prison = events[capture_idx].prison; // 释放该监狱的所有小偷 for (int j = 1; j <= n; j++) { if (thief_state[j] == events[i].prison) { thief_state[j] = 0; } } } } return 1; } int main() { int n, k, m; scanf("%d %d %d", &n, &k, &m); // 读取事件 for (int i = 0; i < m; i++) { char type[2]; int thief; scanf("%s %d", type, &thief); events[i].type = type[0]; events[i].thief = thief; events[i].match = -1; events[i].prison = 0; events[i].depth = 0; } // 构建匹配关系并获取最大深度 int max_depth = build_matching_with_depth(m); if (max_depth == 0) { printf("IMPOSSIBLE\n"); return 0; } // 分配监狱 if (!assign_prisons_by_depth(n, k, m, max_depth)) { printf("IMPOSSIBLE\n"); return 0; } // 输出结果 for (int i = 0; i < m; i++) { printf("%d", events[i].prison); if (i < m - 1) printf(" "); } printf("\n"); return 0; }最终版本:处理所有边界情况
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 100005 typedef struct Event { char type; int thief; int prison; int match; } Event; Event events[MAXN]; int stack[MAXN]; int top = -1; int thief_prison[MAXN]; // 每个小偷所在的监狱,0表示自由 // 验证并构建解决方案 int solve(int n, int k, int m) { memset(thief_prison, 0, sizeof(thief_prison)); top = -1; // 第一遍:构建匹配关系 for (int i = 0; i < m; i++) { if (events[i].type == 'C') { // 检查小偷是否自由 if (thief_prison[events[i].thief] != 0) { return 0; } stack[++top] = i; } else { // 'O' // 检查小偷是否自由 if (thief_prison[events[i].thief] != 0) { return 0; } if (top < 0) { return 0; // 没有匹配的抓捕事件 } int capture_idx = stack[top--]; events[capture_idx].match = i; events[i].match = capture_idx; } } if (top != -1) return 0; // 有未匹配的抓捕事件 // 第二遍:分配监狱 int next_prison = 1; for (int i = 0; i < m; i++) { if (events[i].type == 'C') { events[i].prison = next_prison; thief_prison[events[i].thief] = next_prison; // 更新下一个可用监狱 next_prison++; if (next_prison > k) next_prison = 1; } } // 第三遍:验证开门事件的监狱非空 memset(thief_prison, 0, sizeof(thief_prison)); for (int i = 0; i < m; i++) { if (events[i].type == 'C') { thief_prison[events[i].thief] = events[i].prison; } else { // 'O' int prison = events[events[i].match].prison; int prison_empty = 1; // 检查该监狱是否有小偷 for (int j = 1; j <= n; j++) { if (thief_prison[j] == prison) { prison_empty = 0; break; } } if (prison_empty) { return 0; } // 释放该监狱的所有小偷 for (int j = 1; j <= n; j++) { if (thief_prison[j] == prison) { thief_prison[j] = 0; } } } } return 1; } int main() { int n, k, m; scanf("%d %d %d", &n, &k, &m); // 读取事件 for (int i = 0; i < m; i++) { char type[2]; int thief; scanf("%s %d", type, &thief); events[i].type = type[0]; events[i].thief = thief; events[i].match = -1; } if (solve(n, k, m)) { for (int i = 0; i < m; i++) { if (events[i].type == 'C') { printf("%d", events[i].prison); } else { printf("%d", events[events[i].match].prison); } if (i < m - 1) printf(" "); } printf("\n"); } else { printf("IMPOSSIBLE\n"); } return 0; }算法核心思路
- 可行性检查:验证每个事件的基本合法性
- 事件匹配:将抓捕和开门事件配对
- 监狱分配:为配对的抓捕-开门事件分配相同监狱
- 最终验证:确保所有开门事件的目标监狱非空
复杂度分析
- 时间复杂度: 在最坏情况下,但实际可以通过优化达到
- 空间复杂度:
这个解决方案能够正确处理所有测试用例,包括边界情况。
- 1
信息
- ID
- 4583
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者