首页 > 试题广场 >

外卖小哥的保温箱

[编程题]外卖小哥的保温箱
  • 热度指数:3405 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

众所周知,美团外卖的口号是:”美团外卖,送啥都快”。身着黄色工作服的骑手作为外卖业务中商家和客户的重要纽带,在工作中,以快速送餐突出业务能力;工作之余,他们会通过玩智力游戏消遣闲暇时光,以反应速度彰显智慧,每位骑手拿出装有货物的保温箱,参赛选手需在最短的时间内用最少的保温箱将货物装好。

 

我们把问题简单描述一下:

1 每个货物占用空间都一模一样

2 外卖小哥保温箱的最大容量是不一样的,每个保温箱由两个值描述: 保温箱的最大容量 bi ,当前已有货物个数 ai ,(ai<=bi)

3 货物转移的时候,不必一次性全部转移,每转移一件货物需要花费 1秒 的时间


输入描述:

第一行包含n个正整数(1<=n<=100)表示保温箱的数量

第二行有n个正整数a1,a2,…,an(1<=ai<=100)

ai表示第i个保温箱的已有货物个数

第三行有n个正整数b1,b2,…,bn(1<=bi<=100),bi表示第i个保温箱的最大容量

显然,每一个ai<=bi



输出描述:

输出为两个整数k和t, k表示能容纳所有货物的保温箱的最少个数,t表示将所有货物转移到这k个保温箱所花费的最少时间,单位为秒.

示例1

输入

4 
3 3 4 3
4 7 6 5

输出

2 6

说明

我们可以把第一个保温箱中的货物全部挪到第二个保温箱中,花费时间为3秒,此时第二个保温箱剩余容量为1,然后把第四个保温箱中的货物转移一份到第二个保温箱中,转移最后两份到第三个保温箱中.总花费时间也是3秒,所以最少保温箱个数是2,最少花费时间为6秒
示例2

输入

2 
1 1
100 100

输出

1 1
示例3

输入

5 
10 30 5 6 24
10 41 7 8 24

输出

3 11
#include <bits/stdc++.h>
usingnamespacestd;
typedefstruct{
int  x, y;   
#include <bits/stdc++.h>
 
using namespace std;
 
typedef struct {
    int x, y;    //x为箱子个数,y表示选的这些箱子种原有的保温箱的个数
}mytype;
 
int main() {
    int n;
    cin >> n;
    int a[n], b[n], w = 0, ori_w = 0;
    for(auto &e : a) {cin >> e; ori_w += e;}
    for(auto &e : b) {cin >> e; w += e;} 
     
    mytype f[n+1][w+1];//f[i][j]表示在前i个箱子中选择f[i][j].x个箱子拼成j容量的容积,且f[i][j].y为选的这x保温箱的原有个数
    f[0][0] = {0, 0};    //边界条件
    for(int j = 1; j<=w; ++j) f[0][j] = {INT_MAX, 0};    //用无穷大表示拼不出
     
/*
*对于状态f[i][j],即当前要在前i个箱子中选若干个箱子,使得能拼成容积为j的大箱子,对于第i个箱子,我们总共有两种选择,但是
*对于任何一种选择,我们都要得到最小的箱子个数,并且在确定箱子个数的同时还得保证这些箱子里原有保温瓶尽量的多,即移动
*的开销就会越小
*1)方案1:不选第i个,则f[i][j] = f[i-1][j]则要在前i-1个箱子里选
*2)方案2:选第i个,则还需在前i-1个里选择若干个箱子拼成 j-b[i-1],若f[i-1][j].x > f[i-1][j-b[i-1]].x+1&nbs***bsp;          f[i-1][j].x == f[i-1][j-b[i-1]].x+1 && f[i-1][j-b[i-1]].y > f[i-1][j].y 
*           都有 f[i][j] = {f[i-1][j-b[i-1]].x+1, f[i-1][j-b[i-1]].y + a[i-1] };
*/
    for(int i = 1; i<=n; ++i) {
        for(int j = 0; j<=w; ++j) {
            //不选箱子i
            f[i][j] = f[i-1][j];
            if(j>=b[i-1] && f[i-1][j-b[i-1]].x != INT_MAX) {    //前i-1个箱子得拼的成j-b[i-1]
                if(f[i][j].x > f[i-1][j-b[i-1]].x+1) 
                    f[i][j] = {f[i-1][j-b[i-1]].x+1, f[i-1][j-b[i-1]].y + a[i-1]};
                else if(f[i][j].x == f[i-1][j-b[i-1]].x + 1) {
                    if(f[i][j].y < f[i-1][j-b[i-1]].y + a[i-1])
                        f[i][j] = {f[i-1][j-b[i-1]].x+1, f[i-1][j-b[i-1]].y + a[i-1]};
                }
            }
        }
    }
     
    //现在只需选择能容纳ori_w所用最少箱子的那个f[n][j]
    mytype ans = {INT_MAX, 0};
    for(int j = ori_w; j<=w; ++j) {
        if(f[n][j].x != INT_MAX) {
            if(ans.x > f[n][j].x)
                ans = f[n][j];
            else if(ans.x == f[n][j].x) {
                if(ans.y < f[n][j].y) ans = f[n][j];
            }
        }
    }
    cout << ans.x << " " << ori_w - ans.y << endl;
    return 0;
}
编辑于 2020-06-20 21:03:48 回复(0)
对保温箱和最大容量根据最大容量排序,然后滑窗法将左边的已有货物搬到右边,当左指针与右指针相遇时,那么结束。
vector<int> movethings(vector<int> &things,vector<int> &package) {
    //用冒泡排序的方法进行排序
    for (int i = package.size()-1; i >0; i--)
    {
        for (int j = 0; j < i; j++)
        {
            if (package[j] > package[j+1]) {
                swap(package[i],package[j]);
                swap(things[i], things[j]);
            }
        }
    }
    int i=0, j=package.size()-1;
    int time = 0;
    //排序左边的货物搬移到右边
    while (i<j)
    {
        if ((things[i])>(package[j]-things[j])) {
            time = time + package[j] - things[j];
            things[i] = things[i] - (package[j] - things[j]);
            things[j] = package[j];
            j--;
        }
        else
        {
            time = time + things[i];
            things[j] = things[i] + things[j];
            things[i] = 0;
            i++;
        }
    }
    vector<int> v;
    v.push_back(time);
    v.push_back(package.size() - i);
    return v;
}

int main() {
    int n,temp;
    cin >> n;
    vector<int> things,package;
    for(int i=0;i<n;i++)
    {
        cin >> temp;
        things.push_back(temp);
    }
    for (int i = 0; i < n; i++)
    {
        cin >> temp;
        package.push_back(temp);
    }
    vector<int> v =movethings(things, package);
    cout<<v[1]<<" "<<v[0]<<endl;
}
发表于 2020-03-08 21:54:31 回复(9)
如果只能过30%是牛客问题的话,我这个解法应该是最好的,贪心一般就是要比DP要好很多。

n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))

map = {}
pick = []

for i in range(len(b)):
    map[b[i]] = i

sum = sum(a)         # 计数总共需要的容量
b.sort(reverse=True) # 按容量从大到小排列 贪心法 贪心策略 优先选容量最大的

i = 0
while sum > 0 and i < len(b): # 按容量从大到小选保温箱 
    sum -= b[i]
    pick.append(map[b[i]])    # 保存保温箱在的索引
    i += 1
need = 0
for i in range(len(a)):
    if i in pick:             # 遇到已选择的保温箱 跳过
        continue
    else:
        need += a[i]          # 没选这个保温箱 需要移动 加到need

print(need)                   # need 保存了所有需要移动的容量


编辑于 2020-08-18 15:00:51 回复(3)
题目自身有小问题。testcase 在30%开始输入格式有错误,应该是3行的输入,却错误地变成了2行
···
30
29 3 2 13 3 12 73 22 37 48 59 17 2 13 69 43 32 14 4 2 61 22 40 30 1 4 46 5 65 17 (行末的换行符没了)55 3 3 92 25 27 97 40 55 74 91 31 7 33 72 62 61 40 16 2 70 61 67 72 8 5 48 9 75 84
···
下一个testcase的输入也有这个问题。
发表于 2020-04-04 13:57:05 回复(2)
import copy

n = int(input().strip())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
b_tmp = copy.deepcopy(b)
b_tmp.sort()
bb = []#存储最大的k个b_i
for i in range(n):
    bb.append(b_tmp[n-1-i])
    if sum(bb) >= sum(a):
        break
k = i + 1

count = 0
aa = []  #存储符合选中b_i的a_i:>=k个
len_aa = len(aa)
for i in range(n):
    if b[i] in bb:
        aa.append(a[i])
for i in range(len(bb)):
    if (bb[-1] == bb[len(bb)-1-i]):
        count += 1
a_last = []
for i in range(n):
    if b[i] == bb[-1]:
        a_last.append(a[i])
        del aa[aa.index(a[i])]  #删掉了 所以aa有重复的也没事
a_last.sort()

a_last = a_last[0:count]
t = sum(a) - sum(aa) - sum(a_last)
print(k,t)

编辑于 2020-03-14 19:22:08 回复(1)
if __name__ == '__main__':
    n = int(input())
    goods = list(map(int, input().split()))
    cap = list(map(int, input().split()))
    sum_goods = sum(goods)
    sum_cap = sum(cap)
    weight = [0] * (sum_cap+1)
    dp = [sum_cap] * (sum_cap+1)
    dp[0] = 0


    for i in range(n):
        for j in range(sum_cap, 0, -1):
            res = max(j-cap[i], 0)
            if dp[j] < dp[res]+1:
                continue
            elif dp[j] > dp[res]+1:
                dp[j] = dp[res]+1
                weight[j] = weight[res]+goods[i]
            else:
                weight[j] = max(weight[j], weight[res]+goods[i])


    k = sum_cap
    max_weight = 0
    for i in range(sum_goods, sum_cap+1):
        if dp[i] < k:
            k = dp[i]
            max_weight = weight[i]
        elif dp[i] == k:
            max_weight = max(max_weight, weight[i])
    t = sum_goods-max_weight
    print('%d %d' % (k, t))

发表于 2022-09-01 11:35:38 回复(0)
# 我用贪心只能过3个测试用例,总共10个测试用例。。。。(只能保证最少的保温箱数,不能保证最少的转移时间?)
#  我觉得没问题啊,求指教
def run(already,max_size):
    '''
    already: 每个保温箱中已有货物数
    max_size: 每个保温箱最多能容纳的货物数

    return:
        装完所有货物所需最少的保温箱数+货物转移花费的最少时间
    '''
    n=len(max_size)#n=len(already) 保温箱的个数

    total=sum(already)# 货物总数
    
    remain=total# 剩余未装的货物数

    # 按照保温箱的容量对already从大到小排序
    info=zip(max_size,already)
    info=sorted(info,key=lambda x:x[0],reverse=True)
    # print(info)
    min_time=0# 装完所有货物所需最少的时间
    min_boxes_num=0# 装完所有货物所需最少的保温箱数

    # 求解装完所有货物所需最少的保温箱数min_boxes_num
    for i in range(n):
        size,alrea=info[i]# size:当前保温箱的最大容量,alrea:当前保温箱中已装货物的数量

        min_boxes_num+=1
        remain-=size
        if remain<=0:
            break

    # 求解装完所有货物所需最少的时间
    min_box_all_size=0# 已经选择好的min_boxes_num个保温箱中已经装入的总的货物数量
    for i in range(min_boxes_num):
        size,alrea=info[i]# size:当前保温箱的最大容量,alrea:当前保温箱中已装货物的数量
        min_box_all_size+=alrea
    min_time=total-min_box_all_size# 全部货物数量-已经装好的货物数量=需要转移的货物数量=转移所需最少时间

    # 到这里,这个最少时间还不一定是最少的?
    

    return min_boxes_num, min_time


while True:
    try:
        n=int(input())
        already=list(map(int,input().split(' ')))
        max_size=list(map(int,input().split(' ')))
        res=run(already,max_size)
        print(res[0],res[1])
    except:
        break

编辑于 2022-06-11 21:24:39 回复(0)
无语了,测试示例出现了三行……
我说为什么一直卡着,每次都只能再次挑战的时候才能看到测试示例。

发表于 2022-03-31 10:06:43 回复(0)
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    int sum_goods = 0;
    int sum_cap = 0;
    vector<int> goods(n,0);
    vector<int> cap(n,0);
    for(int i = 0;i < n; i++){
        cin >> goods[i];
        sum_goods += goods[i];
    }
    for(int i = 0;i < n ; i++){
        cin >> cap[i];
        sum_cap += cap[i];
    }
    vector<int> dp(sum_cap + 1, sum_cap);
    dp[0] = 0;
    vector<int> weight(sum_cap + 1, 0);

   for (int i = 0; i < n; i++) {
       for (int j = sum_cap; j > 0; j--) {
           int res = max(j - cap[i], 0);
           if (dp[j] < dp[res] + 1) continue;
           else if (dp[j] > dp[res] + 1) {
               dp[j] = dp[res] + 1;
               weight[j] = weight[res] + goods[i];
           } else {
               weight[j] = max(weight[j], weight[res] + goods[i]);
           }
        }
   }

    int k = sum_cap;
    int max_weight = 0;

    for(int i = sum_goods; i <= sum_cap; i++){
         if(dp[i] < k){
             k = dp[i];
             max_weight = weight[i];
         }
         else if(dp[i] == k){
             max_weight = max(max_weight,weight[i]);
         }
     }

    cout<< k << " " << sum_goods - max_weight <<endl;

    return 0;
}
发表于 2021-10-07 13:03:08 回复(0)
递归思路
#include<iostream>
#include<map>
using namespace std;
map<pair<int,int>,pair<int,int>> hash_map;
pair<int,int> solution(const int* a,const int* b,const int N,int n,int m){
    pair<int,int> nm=make_pair(n,m);
    if(hash_map.count(nm)){
        return hash_map[nm];
    }
    if(m<=0){
        hash_map[nm]=make_pair(0,0);
        return hash_map[nm];
    }
    if(n==1){
        if(b[0]>=m){
            hash_map[nm]=make_pair(1,a[0]);
        }else{
            hash_map[nm]=make_pair(-1,-1);
        }
        return hash_map[nm];
    }
    pair<int,int> last=solution(a,b,N,n-1,m);
    pair<int,int> last_=solution(a,b,N,n-1,m-b[n-1]);
    if(last.first==-1&&last_.first==-1){
        hash_map[nm]=last;
    }else if(last.first==-1){
        hash_map[nm]=make_pair(last_.first+1,last_.second+a[n-1]);
    }else if(last_.first==-1){
        hash_map[nm]=last;
    }else{
        if(last.first<last_.first+1){
            hash_map[nm]=last;
        }else if(last.first>last_.first+1){
            hash_map[nm]=make_pair(last_.first+1,last_.second+a[n-1]);
        }else{
            hash_map[nm]=make_pair(last.first,max(last.second,last_.second+a[n-1]));
        }
    }
    return hash_map[nm];
}
int main(){
    int n;
    cin>>n;
    int *a=new int[n],*b=new int[n];
    int m=0;
    for(int i=0;i<n;i++){
        cin>>a[i];
        m+=a[i];
    }
    for(int i=0;i<n;i++){
        cin>>b[i];
    }
    pair<int,int> rs=solution(a,b,n,n,m);
    cout<<rs.first<<" "<<(m-rs.second)<<endl;
    return 0;
}


发表于 2020-11-18 12:10:03 回复(0)
将对应的a和b组成元组,随后按照b为key对这些元组进行降序排列,其中前k个b相加刚好大于sum(ai)时的就是所求的k,而后面的保温箱中的外卖都是需要转移到前面的保温箱中,因此对k+1到N个保温箱中的外卖求和便可得到所用的时间。不过这道题的测试用例貌似有点问题,到30%就GG
import sys

n = int(sys.stdin.readline().strip())
f = [int(x) for x in sys.stdin.readline().strip().split()]
r = [int(x) for x in sys.stdin.readline().strip().split()]
x = list(zip(f, r))
x.sort(key=lambda x: x[1], reverse=True)
total_f = sum(f)
temp = 0
k = 0
t = 0
for i in range(n):
    temp = temp + x[i][1]
    k += 1
    if temp >= total_f:
        # 得到最少的外卖盒箱k,但是此时的时间不一定是最少的
        break
for i in range(k, n):
    t += x[i][0]

for i in range(n, k - 1, -1):
    # 得到外卖份数最多的k个外卖箱,且这k个外卖箱的容量满足需求
    temp_f, temp_t = 0, 0
    for j in range(i - k, i):
        temp_f += x[j][1]
        temp_t += x[j][0]
    if temp_f >= total_f:
        tt = total_f - temp_t
        t = tt if t > tt else t
print('%d %d' % (k, t))


发表于 2020-10-15 17:17:18 回复(0)
# 动态规划, dp[i][j] = (k, s),前i个箱子中任选达到总容量为j所需的最小箱子数k和在最小k条件下能达到的最大现存容量 # (选了k个箱子,这k个箱子已经有的容量最大化,使需要转移的最小,花费更少时间) # 初始化 dp = (inf, 0), 对i, dp[i][j] > dp[i-1][j-bi], 前i-1最优基础上,选i达到容量j一定也是最优(只加了一个), 所以如果比最优还大就优化他,相等就选最大现存容量的max(i的现存容量,i-1的现存容量), 空间优化:每次只和前一次i相关,从j开始倒叙遍历(01背包,只用一次)
import sys
n = int(input().strip())
a = list(map(int, sys.stdin.readline().strip().split()))
# 给的输入有格式问题, a b 没有换行
if len(a) == n:
    b = list(map(int, sys.stdin.readline().strip().split()))
else:
    b = a[n:]
    a = a[:n]
    
# 计算容量
sum_a = sum(a)
sum_b = sum(b)
dp = [[float('inf'), 0] for _ in range(sum_b + 1)]
dp[0] = [0,0]
for i in range(n):
    for j in range(sum_b, b[i] - 1, -1):
        if dp[j][0] > dp[j - b[i]][0] + 1:
            dp[j] = [ dp[j - b[i]][0] + 1 , dp[j - b[i]][1] + a[i] ]
        elif dp[j][0] == dp[j - b[i]][0] + 1:
            dp[j][1] = max(dp[j][1], dp[j - b[i]][1] + a[i])

# 已经遍历完所有的箱子组合了,现在要找出满足题给条件的最佳值,k和s,是现在容量大于等于sum_a
res = [float('inf'), 0]
for i in range(sum_a, sum_b + 1):
    # 优先选取最小的k
    if res[0] > dp[i][0]:
        res = dp[i]
    # 再选择最大的现存容量,使需要转移的数量最少
    elif res[0] == dp[i][0]:
        res[1] = max(res[1], dp[i][1])
# 需要转移的就是 箱子原始货物总量减去选好的箱子现存的货量
print(res[0], sum_a - res[1])
	

发表于 2020-10-12 02:46:35 回复(0)
这题到底是用时短优先还是箱子少优先?

发表于 2020-09-11 20:26:11 回复(0)
哪里有错误?
请检查是否存在语法错误或者数组越界非法访问等情况
case通过率为30.00%
n=int(input())
a=[int(x) for x in input().split()]
b=[int(x) for x in input().split()]
m=sum(a)
A=[]
for i in range(n):
    temp=[a[i],b[i]]
    A.append(temp)
A.sort(key=lambda x:x[1],reverse=True)
M=0
b.sort(reverse=True)
i=0
while M<m:
    M+=b[i]
    i+=1
t=0
for j in range(i):
    t+=A[j][0]
rt=m-t
print(str(i)+' '+str(rt))


发表于 2020-03-12 17:55:12 回复(2)
不会用c,用matlab写了一下,但是提示说运行时间超时。思路:将每个保温箱能装的最多货物先倒序排序,求出需要的保温箱的数量,然后将未使用的保温箱里的货物全部搬移,需要的时间是未使用保温箱里目前含有的货物量。
function [k,t]=a(N,a,b)
total_num=sum(a);    % 总的货物量
sum=0;
k=0;
[b_sort,index]=sort(b,'descend');
fori=1:N   
    sum=sum+b_sort(i);
    ifsum>=total_num
       k=i;
       break;
    end
end
s=N-k;
index_shift=index(s+1:end);  % 未使用到的保温箱索引
t=sum(a(index_shift));
end
求指教啊
发表于 2020-03-11 21:43:03 回复(3)
import sys
def goods(n, a, b):
    rest = sum(a)
    index = n - 1
    boxindex = []
    # 冒泡排序
    for i in range(n - 1):
        for j in range(n - i - 1):
            if b[j] > b[j + 1]:
                b[j], b[j + 1] = b[j + 1], b[j]
                a[j], a[j + 1] = a[j + 1], a[j]
            # b相同时优先a大的
            if b[j] == b[j + 1]:
                if a[j] > a[j + 1]:
                    b[j], b[j + 1] = b[j + 1], b[j]
                    a[j], a[j + 1] = a[j + 1], a[j]
    while rest > 0 and index:
        rest -= b[index]
        boxindex.append(index)
        index -= 1
    boxnum = len(boxindex)
    sumachoice = 0
    for i in boxindex:
        sumachoice += a[i]
    count = sum(a) - sumachoice
    return boxnum, count


n = int(input())
lines = sys.stdin.readlines()
#评论说后面有个例子将两行a,b输入放在一行,因此首先判断n之后的输入行数
if len(lines)==1:
    temp = list(map(int,lines[0].split(' ')))
    a = temp[:n]
    b = temp[n:]
else:
    a = list(map(int, lines[0].split(" ")))
    b = list(map(int, lines[1].split(" ")))

boxmum, count = goods(n, a, b)
print(boxmum, count)
加入评论说的ab在同一行后准确率提升10%,还是不对,不整了去看大佬的题解了
编辑于 2020-03-11 15:00:02 回复(7)