UVA 1616 Caravan Robbers(二分 + 小数变分数)

大体题意:

给你n 个线段,要求重新规划每个线段,使得每个线段的长度都一样,并且线段之间没有交点,问线段的最大长度是多少?

思路:

很容易想到二分线段的最大长度,然后看这个长度是否合适,合适就往右划分,不合适就往左划分。

我直接说正解了:

因为是输出分数,我们应该二分的时候用小数,然后小数变分数,找一个最接近的分数即可,枚举分母计算分子就可以。

吐槽:

一开始用的分数类二分,这样肯定不行的,因为精度一高的话,分子分母会很大,很容易爆int 和longlong,不爆的话 还无法得到正确的答案。

因此肯定是二分小数咯。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;

const int maxn = 1e5+7;
const double eps = 1e-10;
typedef long long LL;
int dcmp(double a,double b){
    if (fabs(a-b) < eps) return 0;
    if (a > b) return 1;
    return -1;
}

int n;
struct Node{
    int l,r;
    void read(){
        scanf("%d %d",&l, &r);
    }
    bool operator < (const Node& rhs) const {
        return r < rhs.r || (r == rhs.r && l < rhs.l);
    }
}p[maxn];
int gcd(int a,int b){
    return !b? a : gcd(b,a%b);
}

double mid;

bool solve(){
    double la = -1.0;
    for (int i = 0; i < n; ++i){
        if ( dcmp(p[i].l,la) >= 0){
            la = p[i].l + mid;
        }
        else {
            if ( (la+mid) > p[i].r ) return false;
            la += mid;
        }
    }
    return true;
}
int main(){
    while(~scanf("%d",&n)){
        int Rr=0x3f3f3f3f;
        for (int i = 0; i < n; ++i) {
            p[i].read();
            if (p[i].r - p[i].l < Rr) Rr = p[i].r - p[i].l;
        }
        sort(p,p+n);
        double L = 0,R = Rr*1.0;
        while(R-L > eps){
            mid = (L+R)/2.0;
            if (solve()) L = mid;
            else R = mid;
        }
        L = (L+R)/2.0;
        double ans = 1e18;
        int fz;
        int fm;
        for (int i = 1; i <= 100000; ++i){
            int j = floor(i*L);
            if (dcmp( fabs((j*1.0)/i-L) , ans) == -1) {
                ans = fabs((j*1.0)/i - L);
                fz = j;
                fm = i;
            }
            j = ceil(i*L);

            if (dcmp( fabs((j*1.0)/i-L) , ans) == -1) {
                ans = fabs((j*1.0)/i - L);
                fz = j;
                fm = i;
            }
        }
        int g = gcd(fz,fm);
        fz /= g;
        fm /= g;
        printf("%d/%d\n",fz,fm);
    }
    return 0;
}


/**
3
2 6
1 4
8 12

**/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值