函数 f(x) = | kx + a | + b,当x = -a/k 时最小,函数图像是一个V字形,有最小值。看到这个形状,我们很容易想到用三分法寻找最小值的位置。然而题目中的F(x) 是多个f(x) 的和,如果要用三分法,就必须保证F(x)在最小值左边单调递减(或平),在右边单调递增(或平),否则可能找到别的谷点(函数图形可以参考W的形状)。
这个证明我暂时没想到,但看到题目的标题是整数域三分,那么不管三七二十一先用再说。
三分法的具体做法是,计算区间[l, r]之间的两个三等分点mid1, mid2。比较 F(mid1) 和 F(mid2) 的大小,然后缩小范围(每次缩小三分之一),让其中较小那个点成为新区域的中点。
若 F(mid1) <= F(mid2),区间[l, mid1, mid2, r]变成[l, mid1, mid2-1]。
若F(mid1) > F(mid2),区间[l, mid1, mid2, r]变成[mid1+1, mid2-1]。
这样就保证了较小的那个点始终在新区域内,符合我们的直觉判断。
#include <iostream>
#include <vector>
using namespace std;
struct fun {
int k, a, b;
};
long long getSum(vector<fun>& f, long long x) {
long long ans = 0;
for (fun& fi : f) {
ans += abs(fi.k * x + fi.a) + fi.b;
}
return ans;
}
int main() {
int t, n, l, r;
cin >> t;
while (t--) {
cin >> n >> l >> r;
vector<fun> f(n);
for (int i = 0; i < n; ++i) {
cin >> f[i].k >> f[i].a >> f[i].b;
}
while (l + 2 <= r) {
int diff = (r - l) / 3;
int mid1 = l + diff;
int mid2 = r - diff;
long long sum1 = getSum(f, mid1);
long long sum2 = getSum(f, mid2);
if (sum1 <= sum2) {
r = mid2 - 1;
} else {
l = mid1 + 1;
}
}
cout << min(getSum(f, l), getSum(f, r)) << endl;
}
}
// 64 位输出请用 printf("%lld")



京公网安备 11010502036488号