XTU-OJ 1460-距离

题目描述#

n个在X轴上的点,存在m个区间,点可以安排在这些区间内,坐标为i,j的两个点的距离为|i−j|。 求这些点最小距离的最大值?

比如有三个点,区间为[1,2],[3,4],[5,7], 我们如果这三个点分别安排在1,3,7,这样最小距离为2;如果安排在1,4,7,这样最小距离为3。 这个例子中最小距离的最大值为3。

输入格式#

第一行是一个整数T(1≤T≤50),表示样例的个数。

每个样例的第一行是两个整数n (2≤n≤10000),m (1≤m≤10000)。

以后的m行,每行两个整数a,b, (1≤a<b≤109),表示整数点可以安排的区间。

输出格式#

每行输出一个样例的结果,如果为一个整数,那么输出整数;否则,输出一个分数,并保证分子与分母互质。

样例输入#

2

3 3
1 2
3 4
5 7

3 1
1 2

样例输出#

3
1/2

提示#

区间可能存在相交或者包含。

解题思路:注意本题答案可能是分数,但是分数计算好像不太方便,所以我们用 浮点数来代替计算。 具体步骤: 对区间排序、化简区间、 二分距离(浮点数)、把浮点数化成分数形式。如果分子/分母 的值 最接近 二分找到的那个浮点数,则把该分数定为目标答案。 

分母大小其实是有范围的,最大的也就是 n-1。得出这个结论,是因为区间端点输入的是整数。所以在普通情况下,很多时候 当某个分数条件成立时,必有一个大于它的整数也成立。
比如 题目中 [1,2],[3,4],[5,7] , 距离:2.9 是成立的,则必然 3 也是成立的, 因为区间端点都是整数, 你若有一段距离选择 2.9,那么其他距离段中必然有多出来 0.1 可以被分回来给 2.9,即:把 0.1 补进 2.9 中也是能成立的。 但是如果某个区间 被 p 个点 整分,则答案可以为分数。 具体情况就沿这个思路往下考虑。

AC代码:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int T,n,m,cnt;
struct QuJian {
    double a = 0, b = 0;
}abQJ[10010];

int cmp(const void *p1,const void *p2){
    if ( ((QuJian *)p1)->a != ((QuJian *)p2)->a)
        return ((QuJian *)p1)->a - ((QuJian *)p2)->a;
    return ((QuJian *)p2)->b - ((QuJian *)p1)->b;
}

int gcd(int x,int y){
    return y>0 ? gcd(y,x%y) : x;
}

void hebingQJ()
{
    cnt = 0;
    for (int i = 1; i < m; i ++)
    {
        if (abQJ[i].b < abQJ[cnt].b)  continue;
        if (abQJ[i].a < abQJ[cnt].b)  abQJ[cnt].b = abQJ[i].b;
        else
        {
            cnt ++;
            abQJ[cnt].a = abQJ[i].a;
            abQJ[cnt].b = abQJ[i].b;
        }
    }
}

bool check(double min_len)
{
    int now = 0, num = 1;
    double point = abQJ[0].a;
    while (num < n && now <= cnt)
    { //如果距离相差 大于 min_len;  这里是验证 abQJ[now].b-point == min_len,如两浮点数差值 小于1e-6,我们看作两数相等
        if (abQJ[now].b-point > min_len || fabs(abQJ[now].b-point-min_len) < 1e-6)
        {
            if (abQJ[now].a - point >= min_len)
                point = abQJ[now].a;
            else 
                point += min_len;
            num ++;
        }
        else  now ++;
    }
    if (num < n)  return false;
    return true;
}

int main()
{
    scanf("%d",&T);
    while (T --)
    {
        int fz, fm, gccd;
        double left, right, mid, best;
        scanf("%d %d",&n,&m);
        for (int i = 0; i < m; i ++)   // 输入区间
            scanf("%lf %lf",&abQJ[i].a,&abQJ[i].b);
        qsort(abQJ,m,sizeof(QuJian),cmp); // 给区间按左右端点排序
        hebingQJ(); // 化简区间(合并 内含、相交的区间)

        left = 0, right = abQJ[cnt].b-abQJ[0].a; // 浮点数二分查找
        while (right-left > 1e-8) // 左右相差小于 1e-8, 则把其看成同一个数(精度)
        {
            mid = (left+right) / 2;
            if (check(mid)) left = mid;
            else    right = mid;
        }
        best = 1e9; // 将浮点数化成 分数形式
        for (int i = 1; i < n; i ++)  // 分母取值范围 1 ~ n-1    
        {
            int fz_ = round(left*i);   // 分子
            double t = fabs((double)fz_/i - left); // 分子/分母 - 浮点数。 t代表误差值
            if (t < best)   // 如果误差值更小, 更新更优的 分子分母组合。
            {
                best = t;
                fz = fz_;
                fm = i;
            }
        }
        gccd = gcd(fz,fm);
        fz /= gccd, fm /= gccd;
        if (fm == 1) printf("%d\n",fz);
        else    printf("%d/%d\n",fz,fm);
    }
    return 0;
}

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值