给一个序列 a1, a2 , … , an
。
你可以对这个序列进行操作,每次操作可以选择一个元素,把它加 1,经过不超过 k 次操作之后,希望序列里面的最小值最大。问这个值是多少。
输入格式
第一行两个整数 n, k
。接下来一行 n 个整数,表示 a1, a2 , … , an
。
输出格式
输出一行,一个整数,表示答案。
样例输入1
5 10
1 4 2 6 8
样例输出1
5
样例输入2
1 10000000000000
100000000
样例输出2
10000100000000
数据规模
对于 100% 的数据,满足 。
思路
二分法
题目要求序列中的最小值最大,因此我们可以使用二分查找来寻找最小值的最大值。
二分的区间为 [1, 10^14]
,也就是最小值的最大值的取值范围,因为 k 最大为 10^13
,ai
最大为 10^8
,经过 k 次 + 1,最坏的情况下,最小值的最大值为 10^13 + 10^8
,所以我们这里二分的区间右端点设置为 10^14
。
假设当前二分到的最小值为 x,那么我们需要统计将每个数都加到 x 需要的操作次数,并判断总操作次数是否小于等于 k。如果总操作次数小于等于 k,则说明当前最小值符合要求,因此我们将左边界更新为 x;否则,当前最小值不符合要求,因此我们将右边界更新为 x-1。
AC代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
long long k;
long long a[N];
bool check(long long x) // check 函数用于判断当前最小值是否符合要求
{
long long cnt = 0;
for (int i = 0; i < n; i ++ )
{
if (a[i] < x) // 如果当前数小于最小值,则需要进行操作
cnt += x - a[i]; // 计算需要操作的次数
if (cnt > k) // 如果操作次数超过了 k,则说明当前最小值不符合要求
return false;
}
return true;
}
int main()
{
cin >> n >> k;
for (int i = 0; i < n; i ++ ) cin >> a[i];
long long l = 1, r = 1e14;
while (l < r)
{
long long mid = (l + r + 1) >> 1;
if (check(mid)) l = mid; // 如果当前最小值符合要求,则更新 l
else r = mid - 1; // 否则更新 r
}
cout << l << endl;
return 0;
}
暴力法
只过了1个测试用例,会出现 Time Limit Exceeded
,所以还是得用上面的二分法 :)
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int n;
LL m;
int a[N];
int main() {
cin >> n >> m;
for (int i = 0; i < n; i ++) {
cin >> a[i];
}
while (m--) {
if (*max_element(a, a + n) == *min_element(a, a + n)) {
cout << *min_element(a, a + n) + m + 1 << endl;
return 0;
}
int min_index = min_element(a, a + n) - a;
a[min_index]++;
}
cout << *min_element(a, a + n) << endl;
return 0;
}