一、题意
输入n条线段,线段之间没有包含关系,要求你把每条线段截成原来的一条子线段,使得改变之后的所有线段长度相等且互不相交(端点可以重合)。求每条线段的最大长度,并用分数表示。
二、解析
刚看到要用分数表示时,我是很懵的,但后来仔细一想,其实这只是出题人的虚晃一枪,我们首先求出小数解,然后再转变为分数即可。
如何求得小数解呢?我们很容易发现,这道题直接求出最大解很难,但是判定某个解是否符合题意是容易的,因此这就很容易让我们想到之前讲过的“二分答案”法。之前的二分答案是二分整数值,这道题只要二分double值即可,这样其实简单的多,不需要许多细节处理,唯一要注意的就是double值之间不能直接判定相等,必须只用fabs()结合设定一个常数精度值eps来实现。具体见代码。
三、代码
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100000 + 5;
const double INF = 1e6 + 5, eps = 1e-10;
struct section {
int l, r;
bool operator < (const section& rhs) const {
return l < rhs.l;
}
} a[maxn];
int n;
bool isOK(double len) {
double r = -INF; // 维护一个“上一线段的右端值”
for(int i = 0; i < n; i ++) {
r = max(r, (double)a[i].l) + len;
if(r > a[i].r) return 0; // 超过线段原右端值则说明该长度失败
}
return 1;
}
void output(double d) { // 小数转分数输出的方法
int ans_p = INF, ans_q = 1;
for(int q = 1; q <= n; q ++) {
int p = (int)(d * q + 0.5);
if(fabs((double)ans_p / ans_q - d) > fabs((double)p / q - d)) ans_p = p, ans_q = q;
}
cout << ans_p << "/" << ans_q << endl;
}
int main() {
while(cin >> n) {
for(int i = 0; i < n; i ++) cin >> a[i].l >> a[i].r;
sort(a, a + n);
double l = 0, r = INF;
while(fabs(l - r) > eps) { // 二分答案,double要用fabs进行精度控制
double mid = (l + r) / 2;
if(isOK(mid)) l = mid;
else r = mid;
}
output(l);
}
}四、归纳
- 二分答案也可以在double类型的基础上进行,与整数二分的区别:
- 无需考虑细节
- double值(l、r)之间不能直接判定相等,必须只用fabs()结合设定一个常数精度值eps来实现
- 看到输出分数不要慌张,小数通过枚举分母的方法可以轻松转变为分数,当然是否通过小数转换的方式还得具体题目具体分析
- 看到直接求解困难,但是判定容易的题目,可以考虑使用二分答案法

京公网安备 11010502036488号