CF671B Robin Hood 题解

题目简述:

每天最富的人财富减一,最穷的人财富加一(若有相同,随便选一个),求最后的穷人与富人财富的差值最小是几?

思路:

贪心地想一想,很容易发现我们需要让最大值最小,最小值最大,才可以让最大值减最小值的差最小。


首先暴力做法 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 cix 的天数来变成 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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值