Skip to content

11991: 【原1991】为了虫群

题目

题目描述

author: Menghui Wang 原OJ链接:https://acm.sjtu.edu.cn/OnlineJudge-old/problem/1991

题目描述

Jim Raynor率领的人类的部队已经踏上了异虫的基地——查尔星。 作为异虫的首脑,你决定派出爆虫来迎击人类的部队。

爆虫身上长有充满酸液的囊。 当它接近敌人时,会触发体内的不稳定化学物质进行反应,将自身引爆,向外泼洒强酸。 自爆会摧毁爆虫,但同时也会对半径\(R\)之内(包括距离为\(R\)的点)的敌人造成大量伤害。

你观察到,人类有\(n\)名陆战队员,站成一条直线。 每个陆战队员的坐标是\(x_i\)。 你有\(k\)个爆虫。 爆虫的酸液会对陆战队员造成巨大伤害,将其瞬间消灭。 你可以把每只爆虫安排在直线上的任意位置,然后引爆,从而消灭距离该爆虫不超过\(R\)的所有陆战队员。

为了消灭所有\(n\)个陆战队员,你需要计算,爆虫的爆炸半径\(R\)至少要多少。

输入格式

输入共两行。

第一行是用空格隔开的两个整数,\(n\)和\(k\)。\(1 \leq k < n \leq 100,000\)。

第二行是用空格隔开的\(n\)个实数,表示每个陆战队员的坐标\(x_i\)。\(-10^7 \leq x_i \leq 10^7\)。所有坐标按升序给出。

输出格式

输出共一行。爆虫的最小爆炸半径\(R\)。保留6位小数。

样例输入

5 2
-10.0 -6.0 10 11 15

样例输出

2.500000

样例说明

将第一只爆虫放在坐标\(-8\)处,第二只爆虫放在坐标\(12.5\)处。 这时,只要爆炸半径为\(2.5\),就能消灭所有陆战队员。

数据规模

对于30%的数据,\( n \leq 50 \)。 对于所有的数据,\( n \leq 100,000 \)。

提示

C++中,下面的程序可以输出保留6位的小数:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    double f = 1.23;
    cout << fixed << setprecision(6) << f << endl;
    return 0;
}

限制

时间限制:1000ms,内存限制:50000kb。

BugenZhao's solution

//
// Created by BugenZhao on 2019/3/17.
//
// 二分答案

/*
#include <iostream>
#include <algorithm>
#include <iomanip>

using namespace std;
int n, k;
double minD = 1e7;

void getAnswer(double *x, double currentD, int pos, int remCircle) {
    if (remCircle == 0 && pos != n) return;
    if (currentD > minD) return;
    if (n - pos < remCircle) return;
    if (pos == n) {
        minD = min(currentD, minD);
        return;
    }
    for (int i = pos; i < n; ++i) {
        getAnswer(x, max(currentD, x[i] - x[pos]), i + 1, remCircle - 1);
    }
}

int main() {

    cin >> n >> k;
    double *x = new double[n]{};
    double begin;
    cin >> begin;
    double tmp;
    for (int i = 1; i < n; ++i) {
        cin >> tmp;
        x[i] = tmp - begin;
    }
    getAnswer(x, 0, 0, k);
    cout << fixed << setprecision(6) << minD / 2.0 << endl;
    return 0;
}
*/

#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cmath>

using namespace std;
int n, k;
double minD = 1e7;
double *x;

int getRank(int lo, int hi, double num) {
    if (hi == lo + 1)
        return lo;
    int mi = lo + (hi - lo) / 2;
    if (x[mi] <= num)
        return getRank(mi, hi, num);
    else
        return getRank(lo, mi, num);
}

int main() {
    cin >> n >> k;
    x = new double[n]{};
    double tmp, begin;
    cin >> begin;
    for (int i = 1; i < n; ++i) {
        scanf("%lf", &tmp);
        x[i] = tmp - begin;
    }
    double min = 0.0, mid, max = x[n - 1];
    while (fabs(max - min) >= 1e-7) {
        mid = (min + max) / 2;
        int curr = 0;
        bool flag = false;
        for (int i = 0; i < k; ++i) {
            curr = getRank(curr, n, x[curr] + mid);
            if (curr == n - 1) {
                flag = true;
                break;
            }
            ++curr;
        }
        if (flag) max = mid;
        else min = mid;
    }
    cout << fixed << setprecision(6) << mid / 2.0 << endl;
}

FineArtz's solution

/* 为了虫群 */
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;

const double INF = 10e7 * 3.0;
double x[100005];
int n, k;

bool check(double p){
    int nowl = 1, cnt = 1;
    for (int i = 2; i <= n; ++i){
        if (x[i] - x[nowl] - 2 * p < 10e-8) continue;
        else{
            ++cnt;
            nowl = i;
            if (cnt > k) return false;
        }
    }
    return true;
}
int main(){
    cin >> n >> k;
    for (int i = 1; i <= n; ++i)
        cin >> x[i];
    double ans = INF, lans = INF;
    double l = 0.0, r = x[n] - x[1], m = (l + r) / 2;
    while (l - r < 10e-8){
        if (check(m)){
            lans = ans;
            ans = m;
            r = m;
            if (abs(lans - ans) < 10e-8) break;
        }
        else l = m;
        m = (l + r) / 2;
    }
    cout << fixed << setprecision(6) << ans << endl;
    return 0;
}

ligongzzz's solution

#include "iostream"
#include "cmath"
#include "cstdio"
using namespace std;

constexpr double jd = 1e-7;

double pos[100009] = { 0 };

bool check(double r, int n, int limit) {
    int cnt = 1;
    for (int i = 0, cur_pos = 0; i < n; ++i) {
        if (r < pos[i] - pos[cur_pos]) {
            ++cnt;
            cur_pos = i;
        }
        if (cnt > limit)
            return false;
    }
    return true;
}

int main() {
    int n, k;
    scanf("%d %d", &n, &k);

    for (int i = 0; i < n; ++i)
        scanf("%lf", &pos[i]);

    //开始循环
    double left = pos[0], right = pos[n - 1];
    double r;
    while (true) {
        auto mid = (right + left) / 2;
        r = mid - pos[0];
        if (check(r * 2, n, k)) {
            right = mid;
            if (right - left <= jd) {
                printf("%.6f", r);
                return 0;
            }
        }
        else {
            left = mid;
        }
    }
    return 0;
}

skyzh's solution

#include <iostream>
#include <iomanip>
using namespace std;

double pos[100000];

int main() {
    double L = 0, R = 1e8;
    int N, K;
    scanf("%d%d", &N, &K);
    for (int i = 0; i < N; i++) {
        scanf("%lf", &pos[i]);
    }
    while (L < R - 1e-7) {
        double M = (L + R) / 2.0;
        bool ok = false;
        double R_L = pos[0], R_R = R_L + 2.0 * M;
        int k = 1;
        for (int i = 0; i < N; i++) {
            if (pos[i] <= R_R) continue;
            R_L = pos[i];
            R_R = R_L + 2.0 * M;
            ++k;
            if (k > K) break;
        }
        if (k <= K) ok = true;
        if (ok) 
            R = M;
        else
            L = M;
    }
    cout << fixed << setprecision(6) << R << endl;
    return 0;
}

yyong119's solution

#include <cstdio>
#define MAX_N 100010
using namespace std;
const double error = 1e-7;
int n, k;
double a[MAX_N];
bool check(double step_length) {
    double start_pos = a[0];
    int cnt = 1;
    for (register int i = 1; i < n; ++i) {
        if (a[i] - start_pos <= step_length) continue;
        if ((++cnt) > k) return false;
        start_pos = a[i];
    }
    return true;
}
int main() {
    scanf("%d%d", &n, &k);
    for (register int i = 0; i < n; ++i) scanf("%lf", &a[i]);
    double l = 0, r = a[n - 1] - a[0];
    while (r - l > error) {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    printf("%.6lf", r / 2);
    return 0;
}