快轉到主要內容

基地台

題目連結

題目大意
#

給你數線上幾個服務點,請你任意設置k個基地點,並找出能全部服務點都被服務到的最小直徑

解題過程
#

首先我們先思考,確定直徑的時候如何最小化建造基地的數量?

我們可以發現,假設\(1\)有服務點,並不知道其他點在何處,僅知道\(1\)左邊已經無服務點,且服務直徑為\(3\),那麼我們可以在\(2\)建造基地台,這樣就會服務到1的前提下,能涵蓋右邊最多的範圍。

如此想來可以發現,如果我們從左往右跑,遇到服務點,就以他為左界建造一個基地台,讓他在涵蓋該服務點的前提,能往右涵蓋最多範圍,再往下搜尋,遇到還沒被服務的點,再以前面的規則建造基地台,就可以使基地台數量最小。

於是我們可以寫出這樣的函式來計算在服務直徑為\(r\)的狀態下,所需要建造的最小基地台數量。(注意題目給的服務點無由小排到大,記得先排序方便從左邊跑到右邊)

int ck(int r, vector<int> &v){
    int last=-1, res =0;
    for(int x=0; x<v.size(); x++){
        if(last >= v[x]) continue; //這個點還在上個基地台服務範圍,跳過
        last = v[x] + r; //紀錄上個基地台服務範圍右界
        res++; //所需基地台數量
    }
    return res;
}

接著我們思考如何解決題目問題

我們知道如果服務半徑越大,那麼所需的基地台數量就越少,於是我們可以將服務半徑由\(1\)列到\(10^9\),對應他們所需的基地台數量,這樣基地台數量就會是一個遞減數列

於是我們可以對基地台數量二分搜,找到 所需基地台數量 \( \le k\)的前提下,服務半徑的最小值,也就是這個數列中符合條件最左邊的點

利用上面的函式寫出AC解!

AC Code
#

#include <bits/stdc++.h>

using namespace std;

int ck(int r, vector<int> &v){
    int last=-1, res =0;
    for(int x=0; x<v.size(); x++){
        if(last >= v[x]) continue;
        last = v[x] + r;
        res++;
    }
    return res;
}


signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n, k;
    cin >> n >> k;
    vector<int> v(n);

    for(int x=0; x<n; x++) cin >> v[x];
    sort(v.begin(), v.end());
    int l=0, r=1e9;
    while(l + 1< r){
        int mid = (l+r) >> 1;
        if(ck(mid, v) > k) l = mid;
        else r = mid;
    }
    cout << r;
    return 0;

}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中