2022牛客国庆集训派对day4D题 & 2019牛客暑期多校训练营第六场D题 - Move
题目描述
链接:入口
来源:牛客网
题意:
小刘有 n 个物品要移动,第 i 个物品体积表示为 v i v_i vi, 他可以将所有这些物品装入最多K个相同体积的盒子中。
小刘非常聪明,他使用以下策略来装物品:
-
每次,他都会通过下一个策略将物品放入一个盒子中,然后他会尝试填充另一个盒子。
-
对于每个盒子,他会反复将最大合适体积的未包装物品放入盒子中,直到没有这样的物品可以装在盒子里。
现在,问题是装所有物品所需的这些盒子的最小体积是多少。
我一看这题,这……不就是二分答案嘛,我啪的一下就交了,啪的一下就WA了,很快的,出题人不讲武德,搞偷袭
下面是我WA了的代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 1005;
int vs[N];
bool check(int v, int n,int k) {
if(v < vs[n-1]) return false;
vector<int> arr(vs, vs + n);
int cnt = 1, cpyv = v;
while(!arr.empty()) {
auto it = upper_bound(arr.begin(), arr.end(), v);
int id = it - arr.begin() - 1;
if(id >= 0 && id < arr.size() && v - arr[id] >= 0) {
v -= arr[id];
arr.erase(it - 1);
// for(int v : arr) printf("%d ", v);puts("");
} else {
v = cpyv;
cnt ++;
}
}
return cnt <= k;
}
int main() {
int T, n, k;
scanf("%d", &T);
for(int q = 1; q <= T; q ++) {
scanf("%d%d", &n, &k);
int sum = 0;
for(int i = 0; i < n; i ++) {scanf("%d", &vs[i]), sum += vs[i];}
sort(vs, vs + n);
int l = vs[n-1], r = sum, mid;
while(l < r) {
mid = (l + r) >> 1;
// printf("mid : %d\n", mid);
if(check(mid, n, k)) r = mid;
else l = mid + 1;
}
printf("Case #%d: %d\n", q, l);
}
return 0;
}
思路:若给出盒子体积,可以知道需要多少盒子能装的下所有物品(模拟一次O(n*logn)),显然 符合题意的盒子体积 对应的 盒子数 小于等于 k。
然后……当时我就二分找尽可能小的ans了。。
- 实际上这题盒子体积和需要的盒子数没有单调性(我也是看别人这么说的
贴个官方题解:
- 容易误认为 箱子越大 需要的箱子数量越少,其实可以理解为 箱子体积变大可能导致可以用一个体积较大的物品替换一个已经放入箱子的物品,会打乱后面放入箱子的序列,
具体我也说不清看栗子就懂了。
贴个我的AC代码:
(是的,把二分改枚举,再精确一下下界
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 1005;
int vs[N];
bool check(int v, int n,int k) {
if(v < vs[n-1]) return false;
vector<int> arr(vs, vs + n);
int cnt = 1, cpyv = v;
while(!arr.empty()) {
auto it = upper_bound(arr.begin(), arr.end(), v) - 1;
int id = it - arr.begin();
if(id >= 0 && id < arr.size() && v - arr[id] >= 0) {
v -= arr[id];
arr.erase(it);
// for(int v : arr) printf("%d ", v);puts("");
} else {
v = cpyv;
cnt ++;
}
}
return cnt <= k;
}
int main() {
int T, n, k;
scanf("%d", &T);
for(int q = 1; q <= T; q ++) {
scanf("%d%d", &n, &k);
int sum = 0;
for(int i = 0; i < n; i ++) {scanf("%d", &vs[i]), sum += vs[i];}
sort(vs, vs + n);
int l = sum / k, r = sum / k + vs[n-1] - 1, mid;
// while(l < r) {
// mid = (l + r) >> 1;
// // printf("mid : %d\n", mid);
// if(check(mid, n, k)) r = mid;
// else l = mid + 1;
// }
while(!check(l, n, k)) l ++;
printf("Case #%d: %d\n", q, l);
}
return 0;
}
- 这里写对下界就行,了解上界是为了分析复杂度。
说明:
如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律。