#P3186. 取出尽量少的球(200分)
-
1000ms
Tried: 223
Accepted: 44
Difficulty: 5
所属公司 :
华为od
-
算法标签>二分算法
取出尽量少的球(200分)
题面描述
某部门开展 Family Day 开放日活动,其中有个从桶里取球的游戏,游戏规则如下:
- 有
N个容量一样的小桶等距排开,且每个小桶都默认装了数量不等的小球,每个小桶装的小球数量记录在数组bucketBallNums中。 - 游戏开始时,要求所有桶的小球总数不能超过
SUM,如果小球总数超过SUM,则需对所有的小桶统一设置一个容量最大值maxCapacity,并需将超过容量最大值的小球拿出来,直至小桶里的小球数量小于maxCapacity。 - 如果所有小桶的小球总和小于
SUM,则无需设置容量值maxCapacity,并且无需从小桶中拿球出来,返回结果[]。 - 如果所有小桶的小球总和大于
SUM,则需设置容量最大值maxCapacity,并且需从小桶中拿球出来,返回从每个小桶拿出的小球数量组成的数组。
思路
- 计算总和:首先计算所有小桶中小球的总和
total。 - 判断是否需要取球:如果
total <= SUM,则直接返回空数组[]。 - 确定
maxCapacity:如果total > SUM,则需要找到一个合适的maxCapacity,使得所有小桶中小球的数量不超过maxCapacity,并且所有小桶中小球的总和不超过SUM。 - 计算每个小桶需要取出的球数:对于每个小桶,如果小球数量大于
maxCapacity,则取出多余的小球。
cpp
#include <bits/stdc++.h>
using namespace std;
int sum, n;
vector<int> bucketBallNums;
bool check(int maxCapacity) {
long remain = 0;
for (int i = 0; i < n; i++) {
// 若桶中小球数量超出 maxCapacity,则只能保留 maxCapacity 个
remain += min(bucketBallNums[i], maxCapacity);
}
return remain <= sum;
}
int main() {
cin >> sum >> n;
bucketBallNums = vector<int>(n);
for (int i = 0; i < n; i++) {
cin >> bucketBallNums[i];
}
int bucketBallNums_max = 0;
long bucketBallNums_sum = 0;
for (int i = 0; i < n; i++) {
bucketBallNums_max = max(bucketBallNums_max, bucketBallNums[i]);
bucketBallNums_sum += bucketBallNums[i];
}
// 所有小桶的小球总和小于 SUM,则无需设置容量值 maxCapacity,并且无需从小桶中拿球出来,返回结果[]
if (bucketBallNums_sum <= sum) {
cout << "[]" << endl;
return 0;
}
// 二分范围
int low = 0;
int high = bucketBallNums_max;
// 记录最优解
int limit = 0;
// 二分
while (low <= high) {
int mid = (high - low) / 2 + low;
// 限制每个桶mid容量,看桶中剩余球数量是否小于等于sum
if (check(mid)) {
// 若是,则mid是一个可能解,但不一定是最优解
limit = mid;
// 继续尝试更大限制,即提高下限
low = mid + 1;
} else {
// 若不是,则mid限制下,所有桶剩余球数量任然大于sum,因此应该尝试更小限制,即压低上限
high = mid - 1;
}
}
// 输出打印
cout << "[";
for (int i = 0; i < n; i++) {
// 取出超出部分后,各个桶中剩余球数量
cout << bucketBallNums[i] - min(bucketBallNums[i], limit);
if (i < n - 1) {
cout << ",";
}
}
cout << "]";
return 0;
}
python
# 输入获取
SUM, N = map(int, input().split())
bucketBallNums = list(map(int, input().split()))
def check(maxCapacity):
remain = 0
for i in range(N):
# 若桶中小球数量超出 maxCapacity,则只能保留 maxCapacity 个
remain += min(bucketBallNums[i], maxCapacity)
return remain <= SUM
def solution():
# 所有小桶的小球总和小于 SUM,则无需设置容量值 maxCapacity,并且无需从小桶中拿球出来,返回结果[]
if sum(bucketBallNums) <= SUM:
return "[]"
# 二分范围
low = 0
high = max(bucketBallNums)
# 记录最优解
limit = 0
# 二分
while low <= high:
mid = (low + high) // 2
# 限制每个桶mid容量,看桶中剩余球数量是否小于等于sum
if check(mid):
# 若是,则mid是一个可能解,但不一定是最优解
limit = mid
# 继续尝试更大限制,即提高下限
low = mid + 1
else:
# 若不是,则mid限制下,所有桶剩余球数量任然大于sum,因此应该尝试更小限制,即压低上限
high = mid - 1
# 取出超出部分后,各个桶中剩余球数量
res = list(map(lambda num: str(num - min(num, limit)), bucketBallNums))
return f"[{",".join(res)}]"
# 输出打印
print(solution())
java
import java.util.Scanner;
import java.util.StringJoiner;
public class Main {
public static int sum, n;
public static int[] bucketBallNums;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
sum = sc.nextInt();
n = sc.nextInt();
bucketBallNums = new int[n];
for (int i = 0; i < n; i++) {
bucketBallNums[i] = sc.nextInt();
}
System.out.println(solution());
}
public static String solution() {
int bucketBallNums_max = 0;
long bucketBallNums_sum = 0;
for (int i = 0; i < n; i++) {
bucketBallNums_max = Math.max(bucketBallNums_max, bucketBallNums[i]);
bucketBallNums_sum += bucketBallNums[i];
}
// 所有小桶的小球总和小于 SUM,则无需设置容量值 maxCapacity,并且无需从小桶中拿球出来,返回结果[]
if (bucketBallNums_sum <= sum) {
return "[]";
}
// 二分范围
int min = 0;
int max = bucketBallNums_max;
// 记录最优解
int limit = 0;
// 二分
while (min <= max) {
int mid = (max - min) / 2 + min;
// 限制每个桶mid容量,看桶中剩余球数量是否小于等于sum
if (check(mid)) {
// 若是,则mid是一个可能解,但不一定是最优解
limit = mid;
// 继续尝试更大限制,即提高下限
min = mid + 1;
} else {
// 若不是,则mid限制下,所有桶剩余球数量任然大于sum,因此应该尝试更小限制,即压低上限
max = mid - 1;
}
}
StringJoiner sj = new StringJoiner(",", "[", "]");
for (int i = 0; i < n; i++) {
// 取出超出部分后,各个桶中剩余球数量
bucketBallNums[i] -= Math.min(bucketBallNums[i], limit);
sj.add(bucketBallNums[i] + "");
}
return sj.toString();
}
public static boolean check(int maxCapacity) {
long remain = 0;
for (int i = 0; i < n; i++) {
// 若桶中小球数量超出 maxCapacity,则只能保留 maxCapacity 个
remain += Math.min(bucketBallNums[i], maxCapacity);
}
return remain <= sum;
}
}
题目内容
某部门开展 FamilyDay 开放日活动,其中有个从桶里取球的游戏,游戏规则如下:
有 N 个容量一样的小桶等距排开,且每个小桶都默认装了数量不等的小球,每个小桶装的小球数量记录在数组 bucketBallNums 中,
游戏开始时,要求所有桶的小球总数不能超过 SUM,如果小球总数超过 SUM,则需对所有的小桶统一设置一个容量最大值 maxCapacity,并需将超过容量最大值的小球拿出来,直至小桶里的小球数量小于 maxCapacity。
请您根据输入的数据,计算从每个小桶里拿出的小球数量?
-
限制规则一:所有小桶的小球总和小于 SUM,则无需设置容量值 maxCapacity,并且无需从小桶中拿球出来,返回结果 []
-
限制规则二:如果所有小桶的小球总和大于 SUM,则需设置容量最大值 maxCapacity,并且需从小桶中拿球出来,返回从每个小桶拿出的小球数量组成的数组
输入描述
第一行输入 2 个正整数,数字之间使用空格隔开,其中:
-
第一个数字表示 SUM
-
第二个数字表示 bucketBallNums 数组长度
第二行输入 N 个正整数,数字之间使用空格隔开,表示 bucketBallNums 的每一项
输出描述
从每个小桶里拿出的小球数量,并使用一维数组表示
备注
-
1≤bucketBallNums[i]≤109
-
1≤bucketBallNums.length=N≤105
-
1≤maxCapacity≤109
-
1≤SUM≤109
样例1
输入
14 7
2 3 2 5 5 1 4
输出
[0,1,0,3,3,0,2]
说明
小球总数为 22, SUM=14,超出范围了,需从小桶取球,
maxCapacity=1,取出球后,桶里剩余小球总和为 7,远小于 14
maxCapacity=2,取出球后,桶里剩余小球总和为 13,
maxCapacity=3,取出球后,桶里剩余小球总和为 16,大于 14
因此 maxCapacity 为 2 ,每个小桶小球数量大于 2 的都需要拿出来;
样例2
输入
3 3
1 2 3
输出
[0,1,2]
说明
小球总数为 6,SUM=3,超出范围了,需从小桶中取球,maxCapacity=1,则小球总数为 3,从 1 号桶取 0 个球,2 号桶取 1 个球,3 号桶取 2 个球;
样例3
输入
6 2
3 2
输出
[]
说明
小球总数为 5,SUM=6,在范围内,无需从小桶取球;