这是HNU数据结构课程的实验八,来源于Codeforces 1873E。
原题链接:1873E Building an Aquarium
小希非常喜欢鱼,因此决定建造一个水族箱。他有一块由 n n n 列组成的珊瑚礁,第 i i i 列的高度为 a i a_i ai。接下来,他将围绕这块珊瑚礁建造一个水族箱,具体步骤如下:
如测试样例的第一组,允许使用的水量为 x = 9 x = 9 x=9 个单位,当珊瑚高度为 a = [ 3 , 1 , 2 , 4 , 6 , 2 , 5 ] a=[3,1,2,4,6,2,5] a=[3,1,2,4,6,2,5] 且选择水族箱高度 h = 4 h=4 h=4 时,最终将使用总共 w = 8 w=8 w=8 单位的水,如图所示。
他可以使用最多 x x x 个单位的水来注满这个水族箱,他也希望建造一个最大的水族箱,那么他可以选择的高度 h h h 的最大值是多少?
输入的第一行为一个正整数 t ( 1 ≤ t ≤ 10 4 ) t\ (1 \leq t \leq 10^4) t (1≤t≤104),表示测试数据的组数。
每个测试用例的第一行为两个正整数 n n n 和 x ( 1 ≤ n ≤ 2 × 10 5 , 1 ≤ x ≤ 10 9 ) x\ (1 \leq n \leq 2 \times 10^5, 1 \leq x \leq 10^9) x (1≤n≤2×105,1≤x≤109),表示珊瑚的列数以及小希可以使用的水的最大量。
每个测试用例的第二行为 n n n 个正整数 a i ( 1 ≤ a i ≤ 10 9 ) a_i\ (1 \leq a_i \leq 10^9) ai (1≤ai≤109),表示每列珊瑚的高度。
所有的输入数据在满足约束条件下,保证都能输出一个有效的最大高度。
输出为t行,每行一个正整数,表示每个水族箱满足条件的最大高度 h h h。
5
7 9
3 1 2 4 6 2 5
3 10
1 1 1
4 1
1 4 3 4
6 1984
2 6 5 9 1 8
1 1000000000
1
4
4
2
335
1000000001
第一个测试用例已在题目描述中图示说明。当选择 h = 4 h = 4 h=4 时,需要 8 8 8 单位的水量;但如果将 h h h 提高到 5 5 5,则需要 13 13 13 单位的水量,这超过了给定的 x = 9 x = 9 x=9 单位。因此 h = 4 h = 4 h=4 是最优解。
在第二个测试用例中,我们可以选择 h = 4 h = 4 h=4,为每列添加 3 3 3 单位水量,总共使用 9 9 9 单位的水。可以证明这是最优方案。
第三个测试用例中,我们可以选择 h = 2 h = 2 h=2 并完全用完所有的水量,因此这是最优解。
随着 h h h 增大,需要的水量非严格单调递增,满足使用二分答案的条件,这也是官方题解的做法。
设 L = min 1 ≤ i ≤ n a i L = \min\limits_{1 \leq i \leq n} a_i L=1≤i≤nminai,容易确定二分答案的范围为 [ L , L + x ] [L,L+x] [L,L+x]。
二分时要注意 mid
的取法,当一个 mid
尝试成功后,我们希望新二分区间变为 [ m i d , R ] [mid, R] [mid,R],为保证最后当 L = R − 1 L=R-1 L=R−1 时区间收敛,需要取 mid = (L + R + 1) / 2
。
时间复杂度 O ( n log x ) O(n \log x) O(nlogx)。
#include
using namespace std;
bool check(const vector<int> &a, const int h, const int max_water) {
long long cur{};
for (auto i : a) {
if (i < h) {
cur += h - i;
if (cur > max_water) return 0;
}
}
return 1;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
int t;
cin >> t;
while (t--) {
int n, x;
cin >> n >> x;
vector<int> a(n);
copy_n(istream_iterator<int>(cin), n, a.begin());
long long l = *min_element(a.begin(), a.end());
long long r = l + x;
while (l < r) {
long long mid{(l + r + 1) >> 1};
if (check(a, mid, x)) l = mid;
else r = mid - 1;
}
cout << l << '\n';
}
return 0;
}
实际上本题也可以不用二分答案求解。
先将所有数据排序,然后遍历数组,依次尝试能否以当前 a i a_i ai 作为水族箱高度,若能则更新剩余水量。
当剩余水量不足,将剩余水量补足至前面看高度能增加多少即可。
时间复杂度 O ( n log n ) O(n \log n) O(nlogn)。
比前一种解法简洁很多。
#include
using namespace std;
int main() {
cin.tie(0)->sync_with_stdio(0);
int t;
cin >> t;
while (t--) {
int n, x, i;
cin >> n >> x;
vector<int> a(n);
copy_n(istream_iterator<int>(cin), n, a.begin());
sort(a.begin(), a.end());
for (i = 1; i < n; i++) {
long long inc = 1LL * (a[i] - a[i - 1]) * i;
if (inc <= x) x -= inc;
else break;
}
cout << a[i - 1] + x / i << '\n';
}
return 0;
}