1 条题解
-
0
思路与算法
令目标值为v,则F(x)≤v等价于所有二次函数都≤v。a>0时解二次不等式,得到两个实根之间的闭区间;判别式为负则无解。a=0时按一次不等式求半直线,b也为0时判断常量是否≤v。将所有区间与[0,1000]求交,非空即v可行。可行性随v单调不减,于是对v进行100次二分并输出可行上界。
正确性说明
对a>0的开口向上抛物线,不等式成立区域恰好为根间区间;一次及常量情况分别单独处理,因此每条约束都被准确表示。全部交集非空当且仅当存在同一个x满足所有约束,也就当且仅当min F≤v。这证明二分判定的单调性与等价性。初始下界-5005000不大于任何允许函数在区间内的值,上界100005005000大于区间内所有函数值,100次迭代使数值区间足够窄。
复杂度
每轮O(n)个常数次求根与区间交,共固定100轮,时间O(100n)。输入系数数组初始化O(n),空间O(n),每轮只需常量工作空间。使用long double并对最终接近0的值规范化,按四位小数输出。数据n≤10000、T<10,不保存多组数据。
易错点
必须处理a=0及a=b=0,不能直接除以2a。区间交从[0,1000]开始,最优值可能在端点,也可能为负数。原目录缺少独立三分知识节点,本解实际使用答案二分与二次不等式,正式映射对应实际方法。独立答案枚举各曲线顶点与两两交点,并以60位Decimal取上包络最小值。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; cout << fixed << setprecision(4); while (T--) { int n; cin >> n; vector<array<long double, 3>> v(n); for (auto &f : v) cin >> f[0] >> f[1] >> f[2]; auto feasible = [&](long double target) { long double l = 0, r = 1000; for (auto f : v) { long double a = f[0], b = f[1], c = f[2] - target; if (a == 0) { if (b == 0) { if (c > 0) return false; } else if (b > 0) r = min(r, -c / b); else l = max(l, -c / b); } else { long double delta = b * b - 4 * a * c; if (delta < 0) return false; long double sq = sqrtl(delta); l = max(l, (-b - sq) / (2 * a)); r = min(r, (-b + sq) / (2 * a)); } if (l > r) return false; } return true; }; long double low = -5005000, high = 100005005000.L; for (int it = 0; it < 100; ++it) { long double mid = (low + high) / 2; if (feasible(mid)) high = mid; else low = mid; } if (fabsl(high) < .00005L) high = 0; if (fabsl(high) < .00005L) high = 0; cout << high << '\n'; } }
- 1