HDU6383(二分最大最小数)

题意

给一个n个数的数列,给一个操作:对于任意两个数a,b(下标不同即可),一个数减2,另一个数加1. 给一个好的序列的定义:如果这个数列最大的数和最小的数相差<=1且每个数都大于0。经过若干次操作后,将这个数列变成好数列,问在所有的情况中,最小数的最大值是多少。

分析

最小数的最大值= =,显然的二分法,当时竟然没有一脸就反应过来,贼难受。

解法是枚举这个最小数,设其为x,x最小是0,最大设为开始这个数列的最大值就行了。我开始设置的平均值,这样并不对(暂时还没想明白)。然后check函数是重点,一开始没想清楚- -白花了很多时间。

按照题意是说两个可以换一个,枚举的时候我取的数列的最小值x已经确定了,对于那些小于当前x的值,肯定是要多的2个来补的。那我们可以遍历整个数组,看看有多少个需要补的,然后再看多了多少个可以填上去。这里有两个问题,下面一个一个分析。第一个问题是为什么他是单调的,也就是为什么可以二分。因为,x如果很大的话,导致每个都需要其他的来补,肯定不行,那x很小的话,导致每个都多了,那我们可以任取多的两个加到其他的,就减少了一个,这样多的一定可以变少,那最后如果每个只多了一个1怎么办?多的一个1肯定是不能加的,但是题意说整个数列最小的数,而且极差不超过1,显然也是好数列。

代码

#include 
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; iif ((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; icin >> 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;
}

你可能感兴趣的:(二分)