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

UVa 1298 Triathlon

题目描述

铁人三项比赛包含三个连续项目:游泳、骑自行车和跑步。每个选手在这三个项目中的速度是已知的。裁判可以任意选择每个赛段的长度(但不能为零),目标是判断是否能够通过选择合适的赛段长度,使得某个特定的选手赢得比赛(即他是唯一的第一名)。

输入格式

  • 第一行:整数 NNN (1≤N≤1001 \leq N \leq 1001N100),表示选手数量
  • 接下来 NNN 行:每行三个整数 Vi,Ui,WiV_i, U_i, W_iVi,Ui,Wi (1≤Vi,Ui,Wi≤100001 \leq V_i, U_i, W_i \leq 100001Vi,Ui,Wi10000),表示第 iii 个选手在三个项目中的速度

输出格式

  • 对于每个选手,输出一行:如果可以让他获胜输出 Yes,否则输出 No

题目分析

问题本质

设三个赛段的长度分别为 x,y,z>0x, y, z > 0x,y,z>0,第 iii 个选手的总时间为:

Ti=xVi+yUi+zWiT_i = \frac{x}{V_i} + \frac{y}{U_i} + \frac{z}{W_i}Ti=Vix+Uiy+Wiz

我们希望对于某个特定选手 kkk,存在 x,y,z>0x, y, z > 0x,y,z>0 使得:

Tk<Ti∀i≠kT_k < T_i \quad \forall i \neq kTk<Tii=k

数学建模与简化

由于 x,y,zx, y, zx,y,z 可以任意缩放,我们可以进行归一化处理。设总长度 s=x+y+zs = x + y + zs=x+y+z,令:

X=xs,Y=ys,Z=zsX = \frac{x}{s}, \quad Y = \frac{y}{s}, \quad Z = \frac{z}{s}X=sx,Y=sy,Z=sz

则有 X+Y+Z=1X + Y + Z = 1X+Y+Z=1,且 X,Y,Z>0X, Y, Z > 0X,Y,Z>0

选手 iii 的总时间可以表示为:

Ti=s(XVi+YUi+ZWi)T_i = s \left( \frac{X}{V_i} + \frac{Y}{U_i} + \frac{Z}{W_i} \right)Ti=s(ViX+UiY+WiZ)

由于 s>0s > 0s>0,比较 Tk<TiT_k < T_iTk<Ti 等价于比较:

XVk+YUk+ZWk<XVi+YUi+ZWi\frac{X}{V_k} + \frac{Y}{U_k} + \frac{Z}{W_k} < \frac{X}{V_i} + \frac{Y}{U_i} + \frac{Z}{W_i}VkX+UkY+WkZ<ViX+UiY+WiZ

代入 Z=1−X−YZ = 1 - X - YZ=1XY,整理得:

X(1Vk−1Vi−1Wk+1Wi)+Y(1Uk−1Ui−1Wk+1Wi)+(1Wk−1Wi)<0X\left(\frac{1}{V_k} - \frac{1}{V_i} - \frac{1}{W_k} + \frac{1}{W_i}\right) + Y\left(\frac{1}{U_k} - \frac{1}{U_i} - \frac{1}{W_k} + \frac{1}{W_i}\right) + \left(\frac{1}{W_k} - \frac{1}{W_i}\right) < 0X(Vk1Vi1Wk1+Wi1)+Y(Uk1Ui1Wk1+Wi1)+(Wk1Wi1)<0

令:

  • Ai=1Vk−1Vi−1Wk+1WiA_i = \frac{1}{V_k} - \frac{1}{V_i} - \frac{1}{W_k} + \frac{1}{W_i}Ai=Vk1Vi1Wk1+Wi1
  • Bi=1Uk−1Ui−1Wk+1WiB_i = \frac{1}{U_k} - \frac{1}{U_i} - \frac{1}{W_k} + \frac{1}{W_i}Bi=Uk1Ui1Wk1+Wi1
  • Ci=1Wk−1WiC_i = \frac{1}{W_k} - \frac{1}{W_i}Ci=Wk1Wi1

则不等式简化为:

AiX+BiY+Ci<0A_i X + B_i Y + C_i < 0AiX+BiY+Ci<0

几何解释

问题转化为:在区域 X>0,Y>0,X+Y<1X > 0, Y > 0, X + Y < 1X>0,Y>0,X+Y<1 内,是否存在点 (X,Y)(X, Y)(X,Y) 满足所有 N−1N-1N1 个不等式。

这等价于求这些半平面的交集是否在可行区域内非空。

算法设计

核心思路

使用半平面交算法判断所有约束条件是否在可行区域内存在公共解。

关键优化

  1. 提前淘汰:如果存在选手在所有三个项目上都不比当前选手差(即 Vj≥Vk,Uj≥Uk,Wj≥WkV_j \geq V_k, U_j \geq U_k, W_j \geq W_kVjVk,UjUk,WjWk),那么当前选手不可能获胜。

  2. 跳过无用约束:如果某个选手在所有三个项目上都严格比当前选手慢,那么该约束总是成立,无需加入半平面交计算。

  3. 数值稳定性:使用较大的缩放因子(10510^5105)来避免浮点数精度问题。

难点与易错点

  1. 浮点数精度:直接使用原始速度的倒数进行计算容易产生精度误差,需要适当缩放。

  2. 边界处理:需要正确处理 X=0X = 0X=0Y=0Y = 0Y=0X+Y=1X + Y = 1X+Y=1 这三条边界线。

  3. 退化情况:当所有约束导致可行区域为空时,需要准确判断。

  4. 平行线处理:在半平面交算法中需要正确处理平行的约束直线。

复杂度分析

  • 对于每个选手,最多需要处理 N−1N-1N1 个约束
  • 半平面交算法的时间复杂度为 O(Nlog⁡N)O(N \log N)O(NlogN)
  • 总时间复杂度为 O(N2log⁡N)O(N^2 \log N)O(N2logN),在 N≤100N \leq 100N100 时完全可行

参考代码

// Triathlon
// UVa ID: 1298
// Verdict: Accepted
// Submission Date: 2025-10-26
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>
using namespace std;const double EPS = 1e-6;
const double SCALE = 100000.0;  // 数值放大系数,提高计算稳定性// 二维点/向量
struct Point {double x, y;Point(double x = 0, double y = 0) : x(x), y(y) {}
};typedef Point Vector;// 有向直线
struct Line {Point p;      // 直线上一点Vector v;     // 方向向量double angle; // 极角Line() {}Line(Point p, Vector v) : p(p), v(v) { angle = atan2(v.y, v.x); }bool operator<(const Line& l) const { return angle < l.angle; }
};// 浮点数比较
int cmp(double x) { if (fabs(x) < EPS) return 0; return x > 0 ? 1 : -1; }// 向量运算
Vector operator+(Vector a, Vector b) { return Vector(a.x + b.x, a.y + b.y); }
Vector operator-(Vector a, Vector b) { return Vector(a.x - b.x, a.y - b.y); }
Vector operator*(Vector a, double k) { return Vector(a.x * k, a.y * k); }
double cross(Vector a, Vector b) { return a.x * b.y - a.y * b.x; }// 判断点p是否在直线L左侧
bool onLeft(Point p, Line L) { return cross(L.v, p - L.p) > 0; }// 求两直线交点
Point getIntersection(Line a, Line b) {Vector u = a.p - b.p;double t = cross(b.v, u) / cross(a.v, b.v);return a.p + a.v * t;
}// 将直线方程Ax+By+C=0转换为Line结构
Line toLine(double A, double B, double C) {if (fabs(A) < fabs(B)) return Line(Point(0, -C/B), Vector(B, -A));else return Line(Point(-C/A, 0), Vector(B, -A));
}// 半平面交主算法
bool halfplaneIntersect(vector<Line> lines) {int n = lines.size();if (n == 0) return false;sort(lines.begin(), lines.end());vector<Line> q(n + 1);    // 双端队列vector<Point> inter(n + 1); // 交点数组int head = 0, tail = 0;q[tail] = lines[0];for (int i = 1; i < n; i++) {// 删除队尾无效直线while (head < tail && !onLeft(inter[tail - 1], lines[i])) tail--;// 删除队首无效直线while (head < tail && !onLeft(inter[head], lines[i])) head++;q[++tail] = lines[i];// 处理平行直线,保留更优者if (cmp(lines[i].angle - q[tail - 1].angle) == 0) {if (onLeft(lines[i].p, q[tail - 1])) q[--tail] = lines[i];else tail--;}// 计算新交点if (head < tail) inter[tail - 1] = getIntersection(q[tail - 1], q[tail]);}// 最后检查队列首尾while (head < tail && !onLeft(inter[tail - 1], q[head])) tail--;while (head < tail && !onLeft(inter[head], q[tail])) head++;return tail - head > 1;  // 交区域是否非空
}int main() {cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);int n;while (cin >> n) {vector<vector<double>> athletes(n, vector<double>(3));for (int i = 0; i < n; i++) cin >> athletes[i][0] >> athletes[i][1] >> athletes[i][2];for (int i = 0; i < n; i++) {bool canWin = true;vector<Line> lines;for (int j = 0; j < n; j++) {if (i == j) continue;// 淘汰条件:当前选手在所有项目上都不优于对手if (athletes[i][0] <= athletes[j][0] && athletes[i][1] <= athletes[j][1] && athletes[i][2] <= athletes[j][2]) {canWin = false;break;}// 跳过条件:当前选手在所有项目上都优于对手if (athletes[i][0] >= athletes[j][0] && athletes[i][1] >= athletes[j][1] && athletes[i][2] >= athletes[j][2]) {continue;}// 构造不等式:Ti < Tj 对应的半平面double A = SCALE / athletes[j][0] - SCALE / athletes[j][2] - SCALE / athletes[i][0] + SCALE / athletes[i][2];double B = SCALE / athletes[j][1] - SCALE / athletes[j][2] - SCALE / athletes[i][1] + SCALE / athletes[i][2];double C = SCALE / athletes[j][2] - SCALE / athletes[i][2];lines.push_back(toLine(A, B, C));}if (!canWin) { cout << "No\n"; continue; }// 添加边界约束:X>0, Y>0, X+Y<1lines.push_back(Line(Point(0, 0), Vector(1, 0)));   // X >= 0lines.push_back(Line(Point(0, 0), Vector(0, -1)));  // Y >= 0lines.push_back(toLine(-1, -1, 1));                 // X + Y <= 1cout << (halfplaneIntersect(lines) ? "Yes\n" : "No\n");}}return 0;
}

总结

本题通过将优化问题转化为几何问题,利用半平面交算法高效解决了铁人三项的赛段设计问题。关键在于理解如何将时间比较转化为线性不等式,以及如何利用几何算法判断这些不等式的可行性。算法中的优化技巧和数值稳定性处理是解决此类问题的关键。

http://www.dtcms.com/a/532884.html

相关文章:

  • cuda编程笔记(33)--Thrust库的使用
  • Gorm(十一)事务
  • Ubuntu24.04安装好Mysql8后,检查mysql占用的内存和磁盘
  • 阿里云申请域名后网站海外网络加速器
  • Orleans ILifecycleParticipant 生命周期管理详细分析
  • 企业门户网站建设方案后台管理wordpress多级tree分类目录
  • Spring XML AOP配置实战指南
  • 什么人需要网站建设柳州网站开发公司
  • 做纯净系统的网站产品做国外网站有哪些
  • 商贸公司网站建设兴城泳装电子商务网站建设
  • 张祥前统一场论中的洛伦兹变换:多层次的应用与全新内涵
  • 网安面试题收集(4)
  • 高端上海网站设计公司价格wordpress 打赏
  • Yolo_lableimg_env
  • 【09】C语言中的格式输入函数scanf()详解
  • 鼠键共享工具
  • 个人网站备案 拍照装修网店
  • 投资,如何获得估值回归的收益?
  • 专业上海网站建设公司排名住房和城乡建设部网站杂志
  • 边界扫描测试原理 4 -- 保持状态
  • 国外服务器网站打开慢重庆公司起名
  • 个人怎样做旅游网站清新太和做网站
  • 电商系统设计:运费
  • Ceph分布式存储
  • 网站建设销售职责网站开发与运维收费明细
  • 破解空间网站十堰网站建设怎么做
  • 网站价值评估 php东莞住房和建设局网站
  • 11-14强制类型转换
  • redis中的数据类型
  • 2025年10月25日(星期六)骑行哈马者游记