#P2651. 搬运服务器
-
2000ms
Tried: 162
Accepted: 49
Difficulty: 5
所属公司 :
华为
时间 :2025年1月15日-留学生
-
算法标签>贪心算法
搬运服务器
题面简述
题目给定了一个机房的布局,其中有多个机柜,每个机柜有一定数量的服务器需求,并且所有的机柜都排成一条直线。我们需要计算将所有服务器从原点搬运到机柜所需的最小距离。每次搬运时,小明可以携带最多 k 台服务器,并且每次搬运完后需要返回原点。
解题思路
-
输入和输出说明:
- 输入:有 n 个机柜,每个机柜有一个位置(可能为正数或负数),每个机柜需要一定数量的服务器。
- 输出:计算最小的搬运总距离。
-
问题分析:
- 我们要考虑搬运服务器的顺序及搬运的方式。对于每个机柜,小明每次最多可以搬运 k 台服务器,搬运过程需要根据机柜的位置来决定搬运的顺序,尽量让每次搬运的距离最短。
- 小明每次需要返回原点,因此如果搬运的机柜很远,距离将会显著增加。
- 我们需要将正数位置和负数位置的机柜分开处理,分别计算它们的搬运距离。
-
算法设计:
- 正负位置分离:我们可以把机柜的位置分成正数和负数两类,分别进行处理。
- 排序:机柜按照距离从大到小排序。
- 搬运策略:每次从原点出发,尽可能多地搬运 k 台服务器,先搬运到距离原点近的机柜,然后返回原点。
-
计算总距离:
- 对每个机柜,计算需要多少次搬运,然后每次搬运都会增加两倍的该机柜的距离(因为需要来回),总距离为所有搬运距离的总和。因为最后可以不回原点,所以最后要减去最大的距离。
-
复杂度分析:
- 对于每个机柜,我们需要对其位置进行排序。排序的时间复杂度是 O(n log n)。
- 模拟搬运服务器,时间复杂度是 O(n max(m))
代码实现
Python
def min_transport_distance(n, k, positions, demands):
total_distance = 0
# 分别处理正数和负数位置的机柜
positive_cabinets = []
negative_cabinets = []
# 将机柜分为两类,正数位置和负数位置
for i in range(n):
if positions[i] >= 0:
positive_cabinets.append([positions[i], demands[i]])
else:
negative_cabinets.append([-positions[i], demands[i]])
# 先搬运距离最远的机柜,正数位置按距离排序jin
positive_cabinets.sort(key=lambda x: x[0])
negative_cabinets.sort(key=lambda x: x[0])
# 处理正数位置的机柜
max_distance = 0
while len(positive_cabinets) > 0:
kk = k
dis = positive_cabinets[-1][0]
max_distance = max(max_distance, dis)
total_distance += dis * 2
while kk > 0 and len(positive_cabinets) > 0:
num = min(kk, positive_cabinets[-1][1])
kk -= num
positive_cabinets[-1][1] -= num
if positive_cabinets[-1][1] == 0:
positive_cabinets.pop()
# 处理负数位置的机柜
while len(negative_cabinets) > 0:
kk = k
dis = negative_cabinets[-1][0]
max_distance = max(max_distance, dis)
total_distance += dis * 2
while kk > 0 and len(negative_cabinets) > 0:
num = min(kk, negative_cabinets[-1][1])
kk -= num
negative_cabinets[-1][1] -= num
if negative_cabinets[-1][1] == 0:
negative_cabinets.pop()
# 扣除最远的机柜的搬运距离,因为它在两次搬运中被计算了一次
total_distance -= max_distance
return total_distance
# 输入读取
n, k = map(int, input().split()) # n 为机柜数量,k 为每次搬运的最大服务器数量
positions = list(map(int, input().split())) # 每个机柜的位置
demands = list(map(int, input().split())) # 每个机柜需要的服务器数量
# 输出最小距离
print(min_transport_distance(n, k, positions, demands))
Java
import java.util.*;
public class Main {
public static int minTransportDistance(int n, int k, int[] positions, int[] demands) {
int totalDistance = 0;
// 分别处理正数和负数位置的机柜
List<int[]> positiveCabinets = new ArrayList<>();
List<int[]> negativeCabinets = new ArrayList<>();
// 将机柜分为两类,正数位置和负数位置
for (int i = 0; i < n; i++) {
if (positions[i] >= 0) {
positiveCabinets.add(new int[]{positions[i], demands[i]});
} else {
negativeCabinets.add(new int[]{-positions[i], demands[i]});
}
}
// 先搬运距离最远的机柜,正数位置按距离排序
positiveCabinets.sort((a, b) -> Integer.compare(a[0], b[0]));
negativeCabinets.sort((a, b) -> Integer.compare(a[0], b[0]));
// 处理正数位置的机柜
int maxDistance = 0;
while (positiveCabinets.size() > 0) {
int kk = k;
int dis = positiveCabinets.get(positiveCabinets.size() - 1)[0];
maxDistance = Math.max(maxDistance, dis);
totalDistance += dis * 2;
while (kk > 0 && positiveCabinets.size() > 0) {
int num = Math.min(kk, positiveCabinets.get(positiveCabinets.size() - 1)[1]);
kk -= num;
positiveCabinets.get(positiveCabinets.size() - 1)[1] -= num;
if (positiveCabinets.get(positiveCabinets.size() - 1)[1] == 0) {
positiveCabinets.remove(positiveCabinets.size() - 1);
}
}
}
// 处理负数位置的机柜
while (negativeCabinets.size() > 0) {
int kk = k;
int dis = negativeCabinets.get(negativeCabinets.size() - 1)[0];
maxDistance = Math.max(maxDistance, dis);
totalDistance += dis * 2;
while (kk > 0 && negativeCabinets.size() > 0) {
int num = Math.min(kk, negativeCabinets.get(negativeCabinets.size() - 1)[1]);
kk -= num;
negativeCabinets.get(negativeCabinets.size() - 1)[1] -= num;
if (negativeCabinets.get(negativeCabinets.size() - 1)[1] == 0) {
negativeCabinets.remove(negativeCabinets.size() - 1);
}
}
}
// 扣除最远的机柜的搬运距离,因为它在两次搬运中被计算了一次
totalDistance -= maxDistance;
return totalDistance;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 输入读取
int n = scanner.nextInt();
int k = scanner.nextInt();
int[] positions = new int[n];
int[] demands = new int[n];
for (int i = 0; i < n; i++) {
positions[i] = scanner.nextInt();
}
for (int i = 0; i < n; i++) {
demands[i] = scanner.nextInt();
}
// 输出最小距离
System.out.println(minTransportDistance(n, k, positions, demands));
scanner.close();
}
}
C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
int minTransportDistance(int n, int k, vector<int>& positions, vector<int>& demands) {
int totalDistance = 0;
vector<pair<int, int>> positiveCabinets;
vector<pair<int, int>> negativeCabinets;
// 将机柜分为两类,正数位置和负数位置
for (int i = 0; i < n; i++) {
if (positions[i] >= 0) {
positiveCabinets.push_back({positions[i], demands[i]});
} else {
negativeCabinets.push_back({-positions[i], demands[i]});
}
}
// 先搬运距离最远的机柜,正数位置按距离排序
sort(positiveCabinets.begin(), positiveCabinets.end());
sort(negativeCabinets.begin(), negativeCabinets.end());
// 处理正数位置的机柜
int maxDistance = 0;
while (!positiveCabinets.empty()) {
int kk = k;
int dis = positiveCabinets.back().first;
maxDistance = max(maxDistance, dis);
totalDistance += dis * 2;
while (kk > 0 && !positiveCabinets.empty()) {
int num = min(kk, positiveCabinets.back().second);
kk -= num;
positiveCabinets.back().second -= num;
if (positiveCabinets.back().second == 0) {
positiveCabinets.pop_back();
}
}
}
// 处理负数位置的机柜
while (!negativeCabinets.empty()) {
int kk = k;
int dis = negativeCabinets.back().first;
maxDistance = max(maxDistance, dis);
totalDistance += dis * 2;
while (kk > 0 && !negativeCabinets.empty()) {
int num = min(kk, negativeCabinets.back().second);
kk -= num;
negativeCabinets.back().second -= num;
if (negativeCabinets.back().second == 0) {
negativeCabinets.pop_back();
}
}
}
// 扣除最远的机柜的搬运距离,因为它在两次搬运中被计算了一次
totalDistance -= maxDistance;
return totalDistance;
}
int main() {
int n, k;
cin >> n >> k;
vector<int> positions(n);
vector<int> demands(n);
for (int i = 0; i < n; i++) {
cin >> positions[i];
}
for (int i = 0; i < n; i++) {
cin >> demands[i];
}
// 输出最小距离
cout << minTransportDistance(n, k, positions, demands) << endl;
return 0;
}
题目内容
机房中共有n个机柜位于一条直线上,第i个机柜的位置用坐标xi表示,0≤i≤n−1.
现有一批服务器需搬运到n个机柜处,第i个机柜需要mi台服务器。
小明负责搬运工作,小明和所有服务器最初都位于原点0,小明一次最多可以搬运k台服务器。小明必须从原点提取所需数量的服务器,将它们搬运到各自的机柜,然后返回原点提取下一批服务器。
请计算将所有服务器搬运到机柜所需的最小距离。搬运完所有服务器后,
小明无需返回原点。
输入描述
第一行包含两个整数n和k,1≤n≤105,1≤k≤105
第二行包含n个整数,表示n个机柜的坐标 x0,x1,...xn−1,−109≤x≤109
第三行包含n的整数,表示n个机柜所需的服务器数量m0,m1,…mn−1,1≤mi≤10
输出描述
将所有服务器搬运到机柜所需的最小距离。
样例1
输入
4 1
1 3 2 4
1 1 1 1
输出
16
说明
小明每次只能搬1台服务器,因为每次搬运都需要返回原点,小明的最短搬运路径为0→1→0→2→0→3→0→4,将每段路径相加得到 1+1+2+2+3+3+4=16
样例2
输入
5 3
-5 -10 -7 6 8
1 1 1 1 2
输出
26
说明
小明每次可以搬3台服务器,最短搬运路径为0→6→8→0→(−5)→(−7)→(−10),将每段路径相加得到6+2+8+5+2+3=26