From 779ef8b3279069c261ec426ce02ed0266c4b7c3d Mon Sep 17 00:00:00 2001 From: Zengtudor Date: Mon, 20 Oct 2025 11:52:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=A5=B6=E7=89=9B?= =?UTF-8?q?=E7=A2=B0=E6=92=9E=E6=A8=A1=E6=8B=9F=E7=AE=97=E6=B3=95=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现奶牛碰撞模拟算法,包括交叉点检测、排序处理和动态规划计算责备数。该算法通过几何条件筛选潜在碰撞点,按空间位置排序后模拟处理碰撞事件,使用动态规划高效计算每头奶牛的最终责备数。 --- src/10/20/P7150.cpp | 121 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/10/20/P7150.cpp diff --git a/src/10/20/P7150.cpp b/src/10/20/P7150.cpp new file mode 100644 index 0000000..8f00485 --- /dev/null +++ b/src/10/20/P7150.cpp @@ -0,0 +1,121 @@ +#include +#include +#include + +// 为了提高IO效率,这是OI/ACM竞赛中的常用技巧 +#define FAST_IO std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); + +// 奶牛结构体,存储了所有必要信息 +struct Cow { + int id; // 奶牛的原始输入编号 (0 to N-1) + long long x, y; // 初始坐标 + char dir; // 方向 'N' 或 'E' +}; + +// 交叉点/碰撞事件结构体 +struct Intersection { + long long x, y; // 交叉点的坐标 + int north_cow_id; // 参与该交叉点的北向牛的ID + int east_cow_id; // 参与该交叉点的东向牛的ID + + // 重载小于运算符,为sort函数提供排序规则 + // 这是这个算法思想的核心:按空间位置排序 + bool operator<(const Intersection& other) const { + // 主关键字:按 x 坐标从小到大排序 + if (x != other.x) { + return x < other.x; + } + // 次关键字:如果 x 相同,按 y 坐标从小到大排序 + return y < other.y; + } +}; + +int main() { + FAST_IO; + + int N; + std::cin >> N; + + // 分别存储北向和东向的奶牛 + std::vector north_cows; + std::vector east_cows; + // 使用一个vector来存储所有奶牛的原始信息,方便通过ID快速查找 + std::vector all_cows(N); + + for (int i = 0; i < N; ++i) { + char dir; + long long x, y; + std::cin >> dir >> x >> y; + all_cows[i] = {i, x, y, dir}; + if (dir == 'N') { + north_cows.push_back({i, x, y, dir}); + } else { + east_cows.push_back({i, x, y, dir}); + } + } + + // --- 步骤 1: 生成所有潜在的交叉点 --- + std::vector intersections; + for (const auto& n_cow : north_cows) { + for (const auto& e_cow : east_cows) { + // 一个北向牛和一个东向牛要相遇,必须满足几何条件: + // 北向牛在东向牛的右边 (n_cow.x > e_cow.x) + // 且北向牛在东向牛的下边 (n_cow.y < e_cow.y) + if (n_cow.x > e_cow.x && n_cow.y < e_cow.y) { + intersections.push_back({n_cow.x, e_cow.y, n_cow.id, e_cow.id}); + } + } + } + + // --- 步骤 2: 按扫描线顺序对所有交叉点进行排序 --- + // 这是算法的关键!保证了我们总是处理路径上“最先”遇到的障碍 + std::sort(intersections.begin(), intersections.end()); + + // --- 步骤 3: 模拟处理排序后的交叉点 --- + + // `blame`数组既存储最终结果,也动态更新 + // `blame[i]` = 牛i直接或间接阻挡的奶牛总数 + std::vector blame(N, 0); + // `stopped`数组标记一头牛是否已经停止 + std::vector stopped(N, false); + + for (const auto& intersect : intersections) { + int n_id = intersect.north_cow_id; + int e_id = intersect.east_cow_id; + + // 如果参与这个交叉点的两头牛中,有任何一头已经停下了, + // 那么这个交叉点上的碰撞就不会发生,直接跳过。 + if (stopped[n_id] || stopped[e_id]) { + continue; + } + + // 计算两头牛到达交叉点所需的距离(也即时间,因为速度为1) + long long dist_n = intersect.y - all_cows[n_id].y; // 北向牛需要向北走的距离 + long long dist_e = intersect.x - all_cows[e_id].x; // 东向牛需要向东走的距离 + + if (dist_e < dist_n) { // 东向牛先到,阻挡北向牛 + stopped[n_id] = true; // 北向牛被阻挡,标记为停止 + + // 关键的责备计算: + // 东向牛的责备数 += 1 (被它直接阻挡的北向牛) + 北向牛原本的责备数 + // 这是一种动态规划的思想,利用了正确的处理顺序, + // 使得 `blame[n_id]` 此时已经包含了n_id能阻挡的所有下游奶牛。 + blame[e_id] += 1 + blame[n_id]; + + } else if (dist_n < dist_e) { // 北向牛先到,阻挡东向牛 + stopped[e_id] = true; // 东向牛被阻挡,标记为停止 + + // 同理,更新北向牛的责备数 + blame[n_id] += 1 + blame[e_id]; + } + // 如果 `dist_n == dist_e`,两牛同时到达,擦肩而过,互不影响,不做任何处理。 + } + + // --- 步骤 4: 输出结果 --- + // 按照原始输入顺序 (0 to N-1) 输出每头牛的责备数 + for (int i = 0; i < N; ++i) { + std::cout << blame[i] << "\n"; + } + + return 0; +}