题解 | 三分 / 函数 / [ICPC-Chengdu 2010] Error Curves

alt

题干分析

题目要求我们针对一组二次函数(可能退化为一次函数),在区间[0,1000]上取得F(x)的最小值,其中: alt

算法思路

首先我们简单感受一下F(x)是单峰函数: alt 具体的证明有些许繁琐,感兴趣的可以自行查资料了解一下. 现在我们只需要找到在区间[0,1000]下这个单峰函数的最小值.我们因此能够采取三分法进行求解.

精度问题

由于是实数三分,且要求保留小数点后四位,三分精度把控不好是很容易出问题的,这里简单解释一下如何确定精度:

  • 保留小数点后四位,因此x精度至少应为1e-4.
  • a的取值最高为100,范围取值最高为1000,二次项的偏移为axΔx精度至少达到1e-4,所以x精度至少为1e-9.
  • b最高取值为5000,一次项偏移为bΔx,须达到1e-4精度,因此x精度至少为1e-8.
  • 综合起来并留些余量,三分精度定为1e-10最好.

实现代码

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

class P1883 {
    double F(int n, vector<vector<int>> &params, double x) {
        double res = -DBL_MAX;
        for (int i = 0; i < n; i++) {
            double a = params[i][0], b = params[i][1], c = params[i][2];
            res = max(res, a * x * x + b * x + c);
        }
        return res;
    }

public:
    double solve(int n, vector<vector<int>> &params) {
        double left = 0, right = 1000;
        while (right - left > 1e-10) {
            double delta = (right - left) / 3;
            double mid1 = left + delta, mid2 = right - delta;
            double f1 = F(n, params, mid1), f2 = F(n, params, mid2);
            if (f1 <= f2) right = mid2;
            else left = mid1;
        }
        return F(n, params, left);
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    P1883 p;
    for (int _ = 1; _ <= T; _++) {
        int n;
        cin >> n;
        vector params(n, vector<int>(3));
        for (int i = 0; i < n; i++) {
            cin >> params[i][0] >> params[i][1] >> params[i][2];
        }
        printf("%.4lf\n", p.solve(n, params));
    }
    return 0;
}

复杂度分析

  • 时间复杂度: 三分次数共计log(m/b)次,其中m为区间长度,b为精度,每次计算耗时O(n),总时间复杂度为O(nlog(m/b));
  • 空间复杂度: 全过程只消耗常数的额外空间,因此空间复杂度为O(1).
全部评论

相关推荐

昨天 11:52
岭南师范学院
点赞 评论 收藏
分享
12-16 14:57
门头沟学院 Java
迷茫的大四🐶:是这样的,我都拿到你这同一水平的offer了,那我接你的offer的意义在哪,我一开始想接你们的offer期待是很高的,希望你们下次继续努力
你今年的保底offer是...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务