【HNU】数据结构-实验-算法-建造水族馆

这是HNU数据结构课程的实验八,来源于Codeforces 1873E。

原题链接:1873E Building an Aquarium

题目

【问题描述】

小希非常喜欢鱼,因此决定建造一个水族箱。他有一块由 n n n 列组成的珊瑚礁,第 i i i 列的高度为 a i a_i ai。接下来,他将围绕这块珊瑚礁建造一个水族箱,具体步骤如下:

  1. 选择一个整数 h ≤ 1 h \leq 1 h1 作为水族箱的高度。
  2. 在水族箱的两侧建造高度为 h h h 的墙壁。
  3. 然后,向水族箱中注水,使得每一列的水位高度为 h h h,除非珊瑚的高度超过 h h h,此时该列不注水。

如测试样例的第一组,允许使用的水量为 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 单位的水,如图所示。

【HNU】数据结构-实验-算法-建造水族馆_第1张图片

他可以使用最多 x x x 个单位的水来注满这个水族箱,他也希望建造一个最大的水族箱,那么他可以选择的高度 h h h 的最大值是多少?

【输入形式】

输入的第一行为一个正整数 t   ( 1 ≤ t ≤ 10 4 ) t\ (1 \leq t \leq 10^4) t (1t104),表示测试数据的组数。

每个测试用例的第一行为两个正整数 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 (1n2×105,1x109),表示珊瑚的列数以及小希可以使用的水的最大量。

每个测试用例的第二行为 n n n 个正整数 a i   ( 1 ≤ a i ≤ 10 9 ) a_i\ (1 \leq a_i \leq 10^9) ai (1ai109),表示每列珊瑚的高度。

所有的输入数据在满足约束条件下,保证都能输出一个有效的最大高度。

【输出形式】

输出为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=1inminai,容易确定二分答案的范围为 [ 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=R1 时区间收敛,需要取 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;
}

你可能感兴趣的:(#,大一下-数据结构,数据结构,算法,c++)