AtCoder Beginner Contest 157 F - Yakiniku Optimization Problem(思維+計算幾何)

題目鏈接:https://atcoder.jp/contests/abc157/tasks/abc157_f

 

題目大意:

  有n塊餅,有一個熱源,假設熱源在X,Y,那麼第i塊餅加熱所需要的時間是ci(Xxi)2+(Yyi)2c_i*\sqrt{(X-x_i)^2+(Y-y_i)^2},問加熱至少k塊餅需要的最少時間。

 

題目思路:

  首先因爲時間是實數域的,實數的答案在我有限的知識水平中,要麼是唯一確切的答案,能夠通過某個公式或者某種遞推得到,要麼就只有二分or三分。這道題顯然是後者,所以現在需要考慮二分哪個量。這個題目涉及的變量很少,能夠二分的只有兩種可能(在我看來只有兩種),要麼是二分座標,要麼是直接二分時間。可以發現二分座標沒有單調性,所以唯一的可能就是二分時間。如何判斷一個時間是否滿足要求?觀察題目要求的式子,如果對於每個餅,用時間除以cic_i,就是對於這個餅來說要在規定時間內被加熱,熱源得在這個圓裏面。所以問題就轉換爲了,現在有n個圓,是否存在某個點在k個圓內。這個問題卡了我非常久,我怎麼也想不出怎麼處理實數域上的數量統計。後來看了題解才知道,這個點只用枚舉每個圓的圓心和所有的圓之間的交點即可。這是爲什麼呢?因爲可以發現,對於k個圓的重合部分的那個區域,他的邊一定是由這k個圓中的邊緣組成的,那麼在每個轉換的部分,就是某兩個圓的交點。那麼爲什麼還需要考慮圓心呢?考慮這樣一種情況,有兩個圓相交,他們的相交部分裏面有一個非常小的圓(與這兩個圓都不相交),這樣的話,在那個小圓的內部其實是被三個圓覆蓋的,但是這個小圓的內部不是任何兩個圓的交點。

 

以下是代碼:

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN = 5e5+5;
const int MOD= 998244353;
int n,k;
struct node{
    double x,y,c,r;
}a[MAXN];
vector<pair<double,double> >v;
vector<pair<double,double> > cal(double x1,double y1,double r1,double x2,double y2,double r2) {//兩圓交點
    x1-=x2,y1-=y2;
    double S=x1*x1+y1*y1,a=(S+r2*r2-r1*r1)/2,D=S*r2*r2-a*a;
    if(D<0) return {};
    double A1=a*x1,B1=y1*sqrt(D);
    double A2=a*y1,B2=x1*sqrt(D);
    return {{(A1+B1)/S+x2,(A2-B2)/S+y2},{(A1-B1)/S+x2,(A2+B2)/S+y2}};
}
bool check(double t){
    rep(i,1,n)a[i].r=t/a[i].c;
    v.clear();
    rep(i,1,n){
        v.push_back(make_pair(a[i].x,a[i].y));
        rep(j,i+1,n){
            auto p=cal(a[i].x,a[i].y,a[i].r,a[j].x,a[j].y,a[j].r);
            for(auto it:p){
                v.push_back(it);
            }
        }
    }
    int len=v.size();
    rep(i,0,len-1){
        pair<double,double>p=v[i];
        int num=0;
        rep(i,1,n){
            if((a[i].x-p.first)*(a[i].x-p.first)+(a[i].y-p.second)*(a[i].y-p.second)<=a[i].r*a[i].r||
               fabs((a[i].x-p.first)*(a[i].x-p.first)+(a[i].y-p.second)*(a[i].y-p.second)-a[i].r*a[i].r)<=1e-8)num++;
            if(num==k)return 1;
        }
    }return 0;
}
int main(){
    while(cin>>n>>k){
        rep(i,1,n)cin>>a[i].x>>a[i].y>>a[i].c;
        double l=0,r=1e9;
        rep(i,1,100){
            double mid=(l+r)/2;
            if(check(mid))r=mid;
            else l=mid;
        }
        printf("%.10lf\n",r);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章