题意
给一个n个数的数列,给一个操作:对于任意两个数a,b(下标不同即可),一个数减2,另一个数加1. 给一个好的序列的定义:如果这个数列最大的数和最小的数相差<=1且每个数都大于0。经过若干次操作后,将这个数列变成好数列,问在所有的情况中,最小数的最大值是多少。
分析
最小数的最大值= =,显然的二分法,当时竟然没有一脸就反应过来,贼难受。
解法是枚举这个最小数,设其为x,x最小是0,最大设为开始这个数列的最大值就行了。我开始设置的平均值,这样并不对(暂时还没想明白)。然后check函数是重点,一开始没想清楚- -白花了很多时间。
按照题意是说两个可以换一个,枚举的时候我取的数列的最小值x已经确定了,对于那些小于当前x的值,肯定是要多的2个来补的。那我们可以遍历整个数组,看看有多少个需要补的,然后再看多了多少个可以填上去。这里有两个问题,下面一个一个分析。第一个问题是为什么他是单调的,也就是为什么可以二分。因为,x如果很大的话,导致每个都需要其他的来补,肯定不行,那x很小的话,导致每个都多了,那我们可以任取多的两个加到其他的,就减少了一个,这样多的一定可以变少,那最后如果每个只多了一个1怎么办?多的一个1肯定是不能加的,但是题意说整个数列最小的数,而且极差不超过1,显然也是好数列。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn = 3e5 + 5;
ll p[maxn];
int n;
bool check(ll x)
{
ll zheng = 0, fu = 0;
for (int i = 0; i<n; i++)
{
if ((p[i] - x)>0) zheng += ((p[i] - x) / 2);
else if ((x - p[i])>0) fu += (x - p[i]);
}
if (zheng >= fu) return true;
else return false;
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
{
ll maxx=-1;
cin >> n;
for (int i = 0; i<n; i++)
{
cin >> p[i];
maxx =max(p[i],maxx);
}
ll l = 0, r = maxx, ans = 0;
while (r>l)
{
ll mid = (r + l) >> 1;
if (check(mid)) l = mid+1;
else r = mid-1;
}
if (check(l)) cout << l << endl;
else cout << l-1 << endl;
}
return 0;
}