2022牛客国庆集训派对day4D题 & 2019牛客暑期多校训练营第六场D题 - Move

2022牛客国庆集训派对day4D题 & 2019牛客暑期多校训练营第六场D题 - Move

题目描述

链接:入口
来源:牛客网

image-20221004210406719

题意:

小刘有 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了。。


  • 实际上这题盒子体积和需要的盒子数没有单调性(我也是看别人这么说的

贴个官方题解:

image-20221004222256943

  • 容易误认为 箱子越大 需要的箱子数量越少,其实可以理解为 箱子体积变大可能导致可以用一个体积较大的物品替换一个已经放入箱子的物品,会打乱后面放入箱子的序列,具体我也说不清看栗子就懂了

image-20221004224536659

贴个我的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;
}
  • 这里写对下界就行,了解上界是为了分析复杂度。

说明:

如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yaiduil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值