1. 题意
给定一个数组,和一个最多次操作次数。每次操作可以将数组中的一个数 x x x分成两个数 t x − t t\quad x-t tx−t。问 m a x O p e r a t i o n C n t maxOperationCnt maxOperationCnt次操作后,数组中最大的数最小的值是多少。
2. 题解
这个题,我们需要转换思路,不要去想怎么分,而是经过操作后数组中有几个数。对于一个数 x x x,要使它分割后小于 y y y, 我们肯定分割后尽量都分成每个数都为 y y y, 因此最后的堆数为 ⌈ x y ⌉ \lceil \frac{x}{y}\rceil ⌈yx⌉, 分割的次数为 ⌊ x − 1 y ⌋ \lfloor\frac{x-1}{y}\rfloor ⌊yx−1⌋。
再将数组排好序数,进行二分,每次尝试数
x
x
x,看需要的分割数
s
p
l
i
t
C
n
t
splitCnt
splitCnt是否小于等于
m
a
x
O
p
e
r
a
t
i
o
n
C
n
t
maxOperationCnt
maxOperationCnt
s
p
l
i
t
C
n
t
=
∑
x
i
∈
S
⌊
x
i
−
1
y
⌋
S
:
=
{
x
i
,
x
i
>
y
}
splitCnt=\sum_{x_i\in S}\lfloor\frac{x_i-1}{y}\rfloor\quad \\S :=\{x_i,x_i >y\}
splitCnt=xi∈S∑⌊yxi−1⌋S:={xi,xi>y}
- 代码一
class Solution {
public:
int logcnt(int base,int v) {
int ans = 0;
while (base < v) {
v = (v + 1)/2;
ans++;
}
return ans;
}
bool check(const vector<int> &nums, int val,int bid, int maxCnt) {
int sz = nums.size();
int ndcnt = 0;
for (int i = bid;i < sz;i++) {
ndcnt += (nums[i] - 1) / val;
}
return ndcnt <= maxCnt;
}
int FindNotLess(const vector<int> &a, int v) {
int l = 0;
int r = a.size();
while (l < r) {
int mid = (l + r) >> 1;
if (a[mid] <= v)
l = mid + 1;
else
r = mid - 1;
}
return l;
}
int minimumSize(vector<int>& nums, int maxOperations) {
sort(nums.begin(), nums.end());
int sz = nums.size();
int l = 1;
int r = *nums.rbegin();
while (l < r) {
int val = (l + r) >> 1;
// int idx = FindNotLess(nums, val);
vector<int>::iterator it = upper_bound(nums.begin(), nums.end(), val);
int ndCnt = 0;
// for (int i = idx;i < sz;i++) {
// ndCnt += (nums[i] - 1) / val;
// }
for (;it != nums.end(); it++) {
ndCnt += ((*it) - 1)/val;
}
if (ndCnt <= maxOperations) {
r = val;
}
else {
l = val + 1;
}
}
return l;
}
};
其实不需要排序的,直接尝试遍历整个数组就好了
- 03xf的代码
class Solution {
public:
int minimumSize(vector<int>& nums, int maxOperations) {
auto check = [&](int m) -> bool {
long long cnt = 0;
for (int x : nums) {
cnt += (x - 1) / m;
}
return cnt <= maxOperations;
};
int left = 0; // 循环不变量 check(left) == false
int right = ranges::max(nums); // 循环不变量 check(right) == true
while (left + 1 < right) {
int mid = left + (right - left) / 2;
(check(mid) ? right : left) = mid;
}
return right;
}
};
参考
03xf