HDU-6383
题目:
度度熊很喜欢数组!!
我们称一个整数数组为稳定的,若且唯若其同时符合以下两个条件:
1. 数组里面的元素都是非负整数。
2. 数组里面最大的元素跟最小的元素的差值不超过 1。
举例而言,[1,2,1,2] 是稳定的,而 [−1,0,−1] 跟 [1,2,3] 都不是。
现在,定义一个在整数数组进行的操作:
* 选择数组中两个不同的元素 a 以及 b,将 a 减去 2,以及将 b 加上 1。
举例而言,[1,2,3] 经过一次操作后,有可能变为 [−1,2,4] 或 [2,2,1]。
现在给定一个整数数组,在任意进行操作后,请问在所有可能达到的稳定数组中,拥有最大的『数组中的最小值』的那些数组,此值是多少呢?
Input
输入的第一行有一个正整数 T,代表接下来有几组测试数据。
对于每组测试数据:
第一行有一个正整数 N。
接下来的一行有 N 个非负整数 xi,代表给定的数组。
* 1≤N≤3×105
* 0≤xi≤108
* 1≤T≤18
* 至多 1 组测试数据中的 N>30000
Output
对于每一组测试数据,请依序各自在一行内输出一个整数,代表可能到达的平衡状态中最大的『数组中的最小值』,如果无法达成平衡状态,则输出 −1。
Sample Input
2 3 1 2 4 2 0 100000000
Sample Output
2 33333333
记A为原数列,A*为由A变化成的稳定数列,那么题目所求的就是 所有A*中的最小值 中最大的一个,把这个值记为ans。
首先可以先考虑能不能直接暴力枚举所有A*,然后求解...... 很明显是不符合实际的。
换种想法,能不能让ans从无穷大遍历到0,暴力枚举所有ans,然后判断这个ans是否可以构成A*。
那么要先解决判断的问题。
题目要求的是:* 选择数组中两个不同的元素 a 以及 b,将 a 减去 2,以及将 b 加上 1。(这里“不同”指的是下标不同)
也就是进行一次减法(-2),必须要进行一次加法(+1),两种操作的次数要相等。那么可以分开操作,然后再来看两种操作的次数。
我们先对原数列A中所有小于ans的数A[i],做加法,num_p += ans - A[i]
然后对原数列A中所有大于ans+1的数A[i],做减法,num_m += (A[i] - ans) / 2
这里可能会出现A[i] - ans 是奇数的情况,如:(A[i] - ans) / 2 = 3 / 2 = 1,那么其实这时候,A[i]做完减法后得到的数应该是ans+1,它可以作为这个数列A*的最大值,使得数列A*仍然满足 “稳定” 的条件。(min:ans,max:ans+1)
接下来要对num_p(进行加法的次数)和 num_m(进行减法的次数)做比较:
num_p = num_m 的情况:说明这个ans可以作为稳定数列A*的最小值。
num_p != num_m 的情况:
首先要了解的一点是:
对于一个数列A,如果它的最小值为min(A),那么它可以通过题目所述的两种操作,来得到一个稳定数组A*,其最小值仍为min(A)。具体做法是:我们更新数组A,将A中最小值+1,最大值-2,不断重复这个过程。由于减掉的比加上的多,最终数组A中的最大最小值会不断地相互逼近,直到最小值为min(A)。
那么对于num_p < num_m:此时当加法减法配对后,仍有一些数需要进行减法操作,也就是说,此时的数列A最小值已经是ans了,存在一些数大于ans+1,那么根据上面的结论,这个数列A是可以得到稳定数组的,所以这个ans合法。
对于num_p > num_m :此时当加法减法配对后,仍有一些数需要进行加法操作,由于我们进行的是-2,+1,也就是总的来说肯定是朝减少的方向进行,既然出现还要加上的操作,只能说明这个ans还太大了,满足不了,得不到稳定数组。
解决完判断的问题,剩下的就是对ans的范围从原先的 0~∞ 进一步缩减。
很明显的上界应当为数列A的最大值max(A):倘若大于max(A)的话,那么A中所有的数都要往上加才行,由于有减法操作,实现不了;
下界应当为数列A的最小值min(A):在前面红色的结论已经说明了,ans至少会等于min(A)。
那么可以看出采用的算法应该是二分法,边界:min(A)~ max(A)
代码:
//
// main.cpp
// 初赛B1004
//
// Created by jinyu on 2018/8/13.
// Copyright © 2018年 jinyu. All rights reserved.
//
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 3e5+7;
const long long INF = 0x3f3f3f3f3f3f;
long long A[MAXN];
int N;
bool judge(LL ans){
LL num_p = 0;
LL num_m = 0;
for(int i = 0;i<N;++i){
if(ans > A[i])
num_p += ans-A[i];
else if(ans+1 < A[i])
num_m += (A[i]-ans)/2;
}
return num_m>=num_p;
}
int main(){
int T;
scanf("%d",&T);
while (T--) {
scanf("%d",&N);
LL R = -1;
LL L = INF;
for(int i = 0;i<N;++i)
scanf("%lld",&A[i]);
sort(A,A+N);
L = A[0];
R = A[N-1];
LL ans = -1;
while(L<=R){
LL mid = (L+R)/2;
if(judge(mid)){
L = mid + 1;
ans = mid;
}
else
R = mid - 1;
}
printf("%lld\n",ans);
}
return 0;
}