UVA 1616 Caravan Robbers 【二分+貪心+枚舉分母】

題目鏈接

題意

給n個互不相包含的區間,求出一個長度的最大值,使得可以在每個區間中選出這樣一個長度的子區間,這些子區間互不相交。結果用分數表示

分析

先考慮如果給定了區間長度能不能選出這樣的區間。因爲題中說了區間互不包含,所以可以直接把所有區間先按左端點排序再按右端點排序,每個區間都儘量取靠近左端點的子區間。(如果沒有說區間不相互包含的話,就要維護優先隊列)
然後用二分可以求出這個最大長度。這個題卡精度,精度要到1e-9才能過,所以對浮點數二分時候最好直接規定二分次數,不要設EXP,就可以規避卡精度的問題。

最後是用分數表示結果。最開始想的是直接在二分的時候就直接對分母進行二分,但這樣顯然是不正確的,也得不到正確結果。對用分數表示結果的問題一般直接枚舉分母,因爲原區間的長度都是整數,最極端的情況就是1/區間數量,所以從1到n枚舉分母,然後乘以二分出來的結果算出分子。同樣這裏要處理精度問題(算出來的分數化成小數再與二分出來的結果進行比較),結果就是誤差最小的分數。

(疑問:爲什麼算分子是要四捨五入或者用天花板函數?p=round(mid*q) p=celi(mid*q)

AC代碼

//UVA 1616 Caravan Robbers
//AC 2016-8-1 09:31:14
//Greedy, Binary Search, Enumeration
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <list>
#include <sstream>
#include <stack>
using namespace std;

#define cls(x) memset(x,0,sizeof x)
#define inf(x) memset(x,0x3f,sizeof x)
#define neg(x) memset(x,-1,sizeof x)
#define ninf(x) memset(x,0xc0,sizeof x)
#define st0(x) memset(x,false,sizeof x)
#define st1(x) memset(x,true,sizeof x)
#define INF 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define bug cout<<"here"<<endl;
//#define debug

int n;

int gcd(int a,int b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}

struct interval
{
    long double l,r;
    bool operator< (const interval &rhs) const
    {
        if(l==rhs.l)
            return r<rhs.r;
        return l<rhs.l;
    }
}inters[100010];

bool valid(long double x)
{
    long double cur=0;
    for(int i=0;i<n;++i)
    {
        cur=max(cur,inters[i].l);
        cur+=x;
        if(cur>inters[i].r)
            return false;
    }
    return true;
}

int main()
{
    #ifdef debug
        freopen("E:\\Documents\\code\\input.txt","r",stdin);
        freopen("E:\\Documents\\code\\output.txt","w",stdout);
    #endif
    while(cin>>n)
    {
        for(int i=0;i<n;++i)
            cin>>inters[i].l>>inters[i].r;
        sort(inters,inters+n);
        long double a=0,b=1000010,mid;
        for(int i=0;i<100;++i)
        {
            mid=(a+b)/2;
            if(valid(mid))a=mid;
            else b=mid;
        }
        int p,q=1,fp=100100,fq=1;
        for(q=1;q<=n;++q)
        {
            p=round(mid*q);
            if(fabs((long double)p/q-mid)<fabs((long double)fp/fq-mid))
            {
                fp=p;
                fq=q;
            }
        }
        int g=gcd(max(fp,fq),min(fp,fq));
        cout<<fp/g<<"/"<<fq/g<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章