1 条题解
-
0
题意分析
这道题要求判断在理发店服务流程中,对于给定的周期性顾客到达序列,是否可以通过添加有限且相邻的等待时间单位,满足以下条件:
- 每个顾客到达时立即开始服务(不会离开)
- 避免理发师Tom同时服务多个顾客的冲突(学徒的服务可并行)
- 服务流程顺序保持不变:换衣(Change)-洗头(Wash)-理发(Cut)-洗头(Wash)-吹干(Dry)-换衣(Change)-付款(Pay)
关键点:
- 顾客到达时间由输入序列循环累加生成:第一个顾客在时间1,第n个顾客的到达时间 = 1 + 周期和×完整周期数 + 当前周期内的前缀和
- 需要为所有顾客插入相同的等待时间序列(位置和数量固定)
- 添加的等待时间必须是有限的
解题思路
- 计算周期总和T:对输入序列的间隔求和
- 生成前缀和数组:
- 第1个顾客前缀和为0
- 第(i+1)个顾客前缀和 = 前i个间隔之和(共k-1个前缀和)
- 计算所有可能的差集D:
- 遍历前缀和数组,计算所有两两组合的差(s[i] - s[j])
- 检查模T余数覆盖情况:
- 计算D中每个元素对T的余数(映射到[0, T-1])
- 检查余数是否完全覆盖[0, T-1]区间
- 判断结果:
- 如果余数覆盖了整个[0, T-1]区间,输出"No"(无法避免冲突)
- 否则输出"Yes"(存在可行解)
算法分析
- 时间复杂度:对于k个间隔,计算差集和余数检查的时间为O(k² + T),其中k≤20,T≤400,可以高效执行
- 空间复杂度:主要消耗在存储差集和余数数组,O(k² + T)
- 数学原理:当余数覆盖满[0, T-1]时,Tom的服务步骤时间点与顾客到达时间的差值必然冲突,否则可以调整等待时间避免冲突
C++ 代码
#include <iostream> #include <vector> #include <string> #include <algorithm> #include <set> using namespace std; // 解析输入字符串 vector<int> parseInput(string str) { // 去除空格 str.erase(remove_if(str.begin(), str.end(), ::isspace), str.end()); if (str == "(0)") { return vector<int>(); } vector<int> res; string nums = str.substr(1, str.size() - 2); size_t pos = 0; string token; while ((pos = nums.find(',')) != string::npos) { token = nums.substr(0, pos); res.push_back(stoi(token)); nums.erase(0, pos + 1); } if (!nums.empty()) { res.push_back(stoi(nums)); } return res; } int main() { string line; while (getline(cin, line)) { vector<int> a = parseInput(line); // 终止条件 if (a.empty() || (a.size() == 1 && a[0] == 0)) { break; } int k = a.size(); int T = 0; // 计算周期总和 T for (int num : a) { T += num; } // 生成前缀和数组 s vector<int> s; s.push_back(0); // 顾客1的前缀和是0 if (k > 1) { s.push_back(a[0]); // 顾客2的前缀和是 a0 for (int i = 1; i < k - 1; i++) { s.push_back(s.back() + a[i]); } } // 如果 k 为 1,那么只有第一个顾客(s 只包含 0) // 计算差集 D(所有前缀和之间的差值) set<int> D; for (int i = 0; i < s.size(); i++) { for (int j = 0; j < s.size(); j++) { D.insert(s[i] - s[j]); } } // 检查余数覆盖情况 vector<bool> residues(T, false); for (auto d : D) { int r = (d % T + T) % T; // 调整为非负余数 residues[r] = true; } // 检查 [0, T-1] 是否被完全覆盖 bool fullCoverage = true; for (int i = 0; i < T; i++) { if (!residues[i]) { fullCoverage = false; break; } } // 输出结果 cout << (fullCoverage ? "No" : "Yes") << endl; } return 0; }
代码说明
- 输入处理:
parseInput
函数去除空格和括号,提取间隔序列 - 核心计算:
- 循环计算顾客到达时间的周期特性(周期和T,前缀和数组s)
- 通过两重循环计算所有差值的余数分布
- 覆盖检查:
- 使用布尔数组标记存在的余数
- 检查[0, T-1]区间是否被完全覆盖
- 结果判断:
- 完全覆盖 → "No"(无法避免冲突)
- 未完全覆盖 → "Yes"(存在可行解)
- 1
信息
- ID
- 1667
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者