当前位置: 首页 > news >正文

今日一记:五人分鱼与医院值班推理

今天来研究这两个问题:
1. 医院值班逻辑推理问题

核心问题:七位医生(A-G)需在一周内每天安排一人值班,需满足四项条件,且已知F在星期四值班

算法设计特点

  • 约束条件建模:需将条件转化为数学关系(如A=C+1、D=E+2、B=G-3等),并构建变量间的逻辑依赖。
  • 穷举与剪枝结合:由于变量取值有限(7天),采用嵌套循环遍历所有可能排列,但通过条件筛选快速排除无效组合(如F固定为周四,B与G的差值约束)。
  • 空间映射优化:将医生与日期映射为数组索引,通过位置关系直接验证条件(如F在B、C中间需满足B < F < CC < F < B)。 关键难点:条件间存在交叉依赖(如B的时间影响G,G又可能影响其他医生),需保证多条件同时满足。
/*** @file hospital_schedule.c* @brief 医院值班逻辑推理问题:满足约束条件的七医生排班求解*/#include <stdio.h>      // 标准输入输出库(用于printf)
#include <stdbool.h>    // 布尔类型支持(用于bool类型)#define DOCTOR_COUNT 7  // 医生总数
#define DAYS 7          // 一周天数
#define THURSDAY_INDEX 3  // 星期四对应的数组索引(0=周一,3=周四)// 医生编号枚举定义(A=0, B=1, ..., G=6)
typedef enum { A, B, C, D, E, F, G  // 自动分配从0开始的整数值
} Doctor;/*** @brief 验证当前排班是否满足所有约束条件* @param pos 数组,pos[doctor]表示该医生的值班日期索引(0-6对应周一到周日)* @return bool 满足条件返回true,否则false*/
bool validate_conditions(const int pos[]) {// 约束1: A的值班日期比C晚1天(A的位置等于C的位置+1)if (pos[A] != pos[C] + 1) return false;// 约束2: D的值班日期比E晚2天(D的位置等于E的位置+2)if (pos[D] != pos[E] + 2) return false;// 约束3: B的值班日期比G早3天(B的位置等于G的位置-3)if (pos[B] != pos[G] - 3) return false;// 约束4: F在星期四值班(直接检查固定位置)if (pos[F] != THURSDAY_INDEX) return false;// 约束5: F在B和C中间(两种情况:B < F < C 或 C < F < B)if (!((pos[B] < pos[F] && pos[F] < pos[C]) || (pos[C] < pos[F] && pos[F] < pos[B]))) {return false;}return true;  // 所有条件均满足
}/*** @brief 递归生成医生排列并验证条件(回溯算法)* @param doctors 当前已分配的医生数组,doctors[day]表示该天的值班医生* @param used 数组标记已使用的医生,used[doc]=true表示该医生已被安排* @param depth 当前递归深度(0-6对应7天,depth表示正在安排第几天)*/
void generate_schedule(int doctors[], bool used[], int depth) {// 递归终止条件:已安排完所有7天if (depth == DAYS) {int pos[DOCTOR_COUNT] = {0}; // 创建医生->日期的反向映射数组for (int day = 0; day < DAYS; day++) {// 将排班数据转换为pos数组:pos[医生编号] = 值班日期pos[doctors[day]] = day;}// 验证当前排班是否满足所有约束条件if (validate_conditions(pos)) {// 打印有效排班结果printf("Valid Schedule:\n");const char* days[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};for (int i = 0; i < DAYS; i++) {// 输出每天对应的医生字母(A+编号的ASCII转换)printf("%s: Doctor %c\n", days[i], 'A' + doctors[i]);}}return;  // 结束当前递归分支}/*--- 剪枝策略:星期四必须安排医生F ---*/if (depth == THURSDAY_INDEX) {// 如果F尚未被安排if (!used[F]) {doctors[depth] = F;    // 星期四强制安排Fused[F] = true;        // 标记F为已使用generate_schedule(doctors, used, depth + 1);  // 递归安排下一天used[F] = false;       // 回溯:重置F的使用标记}return;  // 结束当前分支(星期四只能安排F)}/*--- 生成其他日期的排列 ---*/for (int doc = 0; doc < DOCTOR_COUNT; doc++) {// 跳过已使用的医生和星期四的F(F已在剪枝策略中处理)if (!used[doc] && doc != F) {doctors[depth] = doc;    // 尝试安排当前医生到depth这一天used[doc] = true;        // 标记该医生为已使用generate_schedule(doctors, used, depth + 1);  // 递归安排下一天used[doc] = false;       // 回溯:重置该医生的使用标记}}
}int main() {int doctors[DAYS] = {0};     // 初始化每天的值班医生数组(0-6对应A-G)bool used[DOCTOR_COUNT] = {false}; // 初始化医生使用标记数组(全部未使用)generate_schedule(doctors, used, 0); // 从第0天(周一)开始生成排列return 0;  // 程序正常退出
}

输出结果: 

 

2. 五人分鱼逆向递推问题

核心问题:五人分鱼每次扔掉1条后平分,求初始最少鱼数使五次操作后剩1条

算法设计特点

  • 逆向数学递推:从最后一步(E分鱼后剩1条)逆推,利用公式x_n = (x_{n+1} * 5)/4 + 1逐层计算初始值。
  • 模运算优化:通过观察发现每次分鱼后剩余数需满足(x-1) % 5 == 0,仅需验证特定步长(如从1开始,步长5)的数值。
  • 终止条件明确:当逆推至初始值满足五次操作均为整数时终止,无需遍历全部可能性。 关键难点:正向暴力枚举效率低,逆向递推需找到递推公式的数学规律。
/*** @file fish_distribution.c* @brief 五人分鱼逆向递推问题:求解满足条件的最小初始鱼数*/#include <stdio.h>      // 标准输入输出(printf)
#include <stdbool.h>    // 布尔类型支持(bool, true, false)#define PEOPLE_COUNT 5  // 分鱼人数(A、B、C、D、E)/*** @brief 验证给定初始鱼数是否满足五次分鱼的条件* @param fish 待验证的初始鱼数* @return bool 满足所有分鱼条件返回true,否则false*/
bool is_valid(int fish) {int temp = fish;    // 保存当前鱼数状态,避免修改原值for (int i = 0; i < PEOPLE_COUNT; i++) {  // 模拟五人依次分鱼// 每次分鱼前必须满足:当前鱼数减1后能被5整除if ((temp - 1) % 5 != 0) { return false; // 不满足条件立即返回失败}// 计算分鱼后剩余量:(当前鱼数-1)后分成5份,拿走1份,剩余4份temp = (temp - 1) / 5 * 4; }// 最终检查:经过五轮分鱼后,剩余鱼数至少为1(题目隐含条件)return temp >= 1; 
}/*** @brief 寻找满足条件的最小初始鱼数* @return int 返回最小鱼数,无解时返回-1(实际上此问题有解)*/
int find_min_fish() {int fish = 1; // 初始鱼数从1开始试探(最小可能值)while (true) { // 无限循环直到找到解if (is_valid(fish)) { return fish; // 找到符合条件的最小值}// 关键优化:每次分鱼前鱼数必须满足fish ≡1 mod5// 因此只需检查形如5k+1的数,步长设为5加速搜索fish += 5; }
}int main() {// 计算并打印最小初始鱼数int min_fish = find_min_fish(); if (min_fish > 0) {// 输出结果:3121(五人分鱼经典问题的解)printf("Minimum initial fish: %d\n", min_fish); } else {printf("No solution found.\n"); // 理论上不会执行此分支}return 0;
}

输出结果:

五人分鱼与医院值班推理
对比维度医院值班问题五人分鱼问题
问题类型约束满足问题(CSP)数论递推问题
核心算法穷举法+条件剪枝逆向数学递推+模运算优化
时间复杂度O(n^4)(多层嵌套循环) 

3

O(k)(k为满足条件的最小值) 

6

空间复杂度O(1)(仅需存储当前排列)O(1)(仅需存储递推中间值)
条件处理方式显式逻辑判断(if语句过滤)隐式数学公式推导(递推公式)
适用场景多变量、多交叉约束的排列问题单变量、线性递推的数学问题
优化策略固定已知变量(如F在周四)减少搜索空间利用模运算缩小候选数范围

 

三、总结

  1. 算法设计差异

    • 医院值班问题依赖逻辑推理与排列组合,需通过显式条件判断快速剪枝,适合处理多变量交叉约束的场景。
    • 五人分鱼问题依赖数学建模与递推公式,通过逆向推导将复杂操作转化为线性计算,适合单变量递推优化。
  2. 效率与复杂度

    • 医院值班问题的嵌套循环导致较高时间复杂度,但通过固定变量(如F)可显著减少计算量
    • 五人分鱼问题的逆向递推避免无效枚举,数学优化使其效率远高于正向暴力搜索 
  3. 方法论启示

    • 约束类问题优先考虑变量间的逻辑关系,利用剪枝或回溯减少搜索空间。
    • 递推类问题优先寻找数学规律(如模运算、递推公式),将操作转化为数值计算。

相关文章:

  • 每日一题(小白)暴力娱乐篇30
  • 简单socket通信,回显 服务器程序与客户端程序之间的通信。
  • linux-vi和文件操作
  • Windows 图形显示驱动开发-WDDM 1.2功能—无显示器系统支持
  • CExercise_13_1排序算法_1插入排序
  • 了解一下Unity的RenderQueue
  • 【基于Servlet技术处理表单】
  • 目标检测:YOLOv11(Ultralytics)环境配置
  • Vue 3 的组合式 API-hooks
  • HTTPS协议原理
  • 软件包安装管理Gitlab
  • PyTorch 根据官网命令行无法安装 GPU 版本 解决办法
  • MyBatis 详解
  • ffmpeg命令(一):信息查询命令
  • 日志查询:使用 less 命令搜索关键字的方法
  • Spring Boot 中使用 Netty
  • .Net 9 webapi使用Docker部署到Linux
  • Quipus,LightRag的Go版本的实现
  • 猫咪如厕检测与分类识别系统系列【九】视频检测区域在线绘制+支持摄像头+网络摄像头+整体构建【上】
  • 怎样完成本地模型知识库检索问答RAG
  • 从 “沪惠保” 到 “沪骑保”看普惠保险的 “上海样式”
  • 51岁国家移民管理局移民事务服务中心联络部副主任林艺聪逝世
  • 张家界乒乓球公开赛设干部职级门槛引关注,回应:仅限嘉宾组
  • 我驻阿巴斯总领馆:将持续跟踪港口爆炸事件进展,全力确保中方人员安全
  • 饶权已任国家文物局局长
  • 价格周报|猪价继续回暖:二次育肥热度仍存,对猪价仍有一定支撑