题目简述:
每天最富的人财富减一,最穷的人财富加一(若有相同,随便选一个),求最后的穷人与富人财富的差值最小是几?
思路:
贪心地想一想,很容易发现我们需要让最大值最小,最小值最大,才可以让最大值减最小值的差最小。
首先暴力做法 k k k 次循环,每一次求出最大值与最小值,将最大值减一,最小值加一,最后输出最大值减最小值即可,时间复杂度: O ( n k ) O(nk) O(nk)。 很明显不可过。
可以考虑先求出最大值,再求出最小值,两者相减即为答案,最大最小值都可以用二分答案求出。
设
f
(
x
)
f(x)
f(x) 为若最后最大值为
x
x
x 是否可以在
k
k
k 天以内得到此最大值:
- 若 c i c_i ci 大于 x x x 就需要花 c i − x c_i - x ci−x 的天数来变成 x x x。
- 否则不变。
用循环枚举每一个 c i c_i ci 累计天数,若天数小于等于 k k k 即为能够在 k k k 天以内得到这个最大值返回 1 1 1 否则返回 0 0 0。
此部分代码如下:
bool check1(int x){//check1(x)就是f(x)
int day = 0;//累计天数的计数器
for(int i = 1; i <= n; i++){
if(c[i] > x) day += c[i] - x;//若大于x则变成x
}
return day <= k;
/*可以写成如下
if(day <= k) return 1;
else return 0;
*/
}
由此我们可以设计出 g ( x ) g(x) g(x) 为若最后最小值为 x x x 是否可以在 k k k 天以内得到此最小值,分析方式与 f ( x ) f(x) f(x) 略同,就不给出来了。
此方法的时间复杂度: O ( n log n ) O(n\log n) O(nlogn)。时间没问题,可以试试。
然后就是疯狂写代码的时候,实现难度还是挺小的,代码如下:
#include <bits/stdc++.h>
#define int long long//不开long long见祖宗
using namespace std;
int n , k , sum , c[500005];
int l1 , r1 , l2 , r2;
bool check1(int x){//f(x)
int day = 0;
for(int i = 1; i <= n; i++){
if(c[i] > x) day += c[i] - x;
}
return day <= k;
}
bool check2(int x){//g(x)
int day = 0;
for(int i = 1; i <= n; i++){
if(c[i] < x) day += x - c[i];
}
return day <= k;
}
signed main(){
scanf("%d%d" , &n , &k);
for(int i = 1; i <= n; i++){
scanf("%d" , &c[i]);
//输入
sum += c[i];
r1 = max(r1 , c[i]);
}
if(sum % n == 0) l1 = sum / n;
else l1 = sum / n + 1;
//最大最小情况为总的财富除以人数向上取整,最大情况为最大的财富
while(l1 < r1){
int mid = (l1 + r1) / 2;
if(check1(mid)) r1 = mid;
else l1 = mid + 1;
}//最大值最小
l2 = 1 , r2 = sum / n;
//最小值最小情况为1次,最大情况为总的财富除以人数向下取整
while(l2 < r2){
int mid = (l2 + r2 + 1) / 2;
if(check2(mid)) l2 = mid;
else r2 = mid - 1;
}//最小值最大
printf("%d" , l1 - l2);//输出
return 0;
}