星际快递(C++/Py/Java /Js/Go)题解
京东技术岗 0314笔试 第一题
题目内容
星际快递公司有 NNN 个包裹需要派送,每个包裹有两种派送方式:
- 常规派送(消耗较多燃料)
- 虫洞派送(使用一个虫洞通行证,可以消耗较少燃料的情况下完成派送)
当前快递飞船携带了 XXX 单位的燃料和 YYY 张虫洞通行证。
星际快递公司想要计算,在优先派送尽可能多包裹的情况下,最小的燃料消耗是多少,请你帮助他们计算一下。
输入描述
第一行三个整数 N,X,YN,X,YN,X,Y ,分别表示包裹数量,携带燃料量以及通行证数量。
接下来 NNN 行,每行两个整数,表示各个包裹常规派送和虫洞派送分别需要的燃料量。
数据范围:
- 1≤N≤1001 ≤ N ≤ 1001≤N≤100
- 1≤X≤50001 ≤ X ≤ 50001≤X≤5000
- 1≤Y≤501 ≤ Y ≤ 501≤Y≤50
- 每个包裹常规派送和虫洞派送的燃料消耗均介于 1,501, 501,50 之间。
输出描述
一行,两个整数,空格分开,表示最多可派送的包裹数量及对应的最小燃料消耗。
样例1
输入
3 20 1
8 5
7 4
10 6
输出
2 12
样例2
输入
4 25 2
10 6
8 5
12 7
9 6
输出
3 20
题解和思路
思路
实现思路:动态规划
- 定义
dp数组其中,dp[i][j]表示完成j个运输,使用k个通行费所需最小燃料。初始全部设置为不可达值,额外定义dp[0][0] = 1 - 从前往后枚举运输任务,从大到小枚举完成任务数 j ,以及从大到小枚举通行证使用数 k,如果
dp[j][k]可达,状态转移方程为:- 常规配送:
dp[j + 1][k] = min(dp[j + 1][k], dp[j][k] + a); - 虫洞运输:
dp[j + 1][k + 1] = min(dp[j + 1][k + 1], dp[j][k] + b);
- 常规配送:
- 按照3的逻辑处理之后,最终可以确定最大运输数量和使用燃料的数量。
- 上述代码的时间复杂度为
O(N X Y)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, X, Y;
cin >> N >> X >> Y;
vector<int> A(N), B(N);
for (int i = 0; i < N; i++) {
cin >> A[i] >> B[i];
}
// dp[j][k] 完成j个运输,使用k个通行费所需最小燃料
vector<vector<int>> dp(N + 1, vector<int>(Y + 1, INT_MAX));
dp[0][0] = 0;
for (int i = 0; i < N; i++) {
int a = A[i], b = B[i];
for (int j = N - 1; j >= 0; j--) {
for (int k = Y; k >=0; k--) {
if (dp[j][k] == INT_MAX) {
continue;
}
// 常规
dp[j + 1][k] = min(dp[j + 1][k], dp[j][k] + a);
// 冲动
if (k + 1 <= Y) {
dp[j + 1][k + 1] = min(dp[j + 1][k + 1], dp[j][k] + b);
}
}
}
}
int bestCnt = 0, minFule = 0;
for (int j = N; j >= 0; --j) {
int cur = INT_MAX;
for (int k = 0; k <= Y; ++k) cur = min(cur, dp[j][k]);
if (cur <= X) {
bestCnt = j;
minFule = cur;
break;
}
}
cout << bestCnt << " " << minFule << endl;
return 0;
}
Java
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int X = Integer.parseInt(st.nextToken());
int Y = Integer.parseInt(st.nextToken());
int[] A = new int[N];
int[] B = new int[N];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
A[i] = Integer.parseInt(st.nextToken());
B[i] = Integer.parseInt(st.nextToken());
}
// dp[j][k] 完成j个运算,使用k个通行费所需最小燃料
int[][] dp = new int[N + 1][Y + 1];
for (int i = 0; i <= N; i++) {
for (int j = 0; j <= Y; j++) {
dp[i][j] = INF;
}
}
dp[0][0] = 0;
for (int i = 0; i < N; i++) {
int a = A[i], b = B[i];
for (int j = N - 1; j >= 0; j--) {
for (int k = Y; k >= 0; k--) {
if (dp[j][k] == INF) {
continue;
}
// 常规
dp[j + 1][k] = Math.min(dp[j + 1][k], dp[j][k] + a);
// 冲动
if (k + 1 <= Y) {
dp[j + 1][k + 1] = Math.min(dp[j + 1][k + 1], dp[j][k] + b);
}
}
}
}
int bestCnt = 0, minFuel = 0;
for (int j = N; j >= 0; j--) {
int cur = INF;
for (int k = 0; k <= Y; k++) {
cur = Math.min(cur, dp[j][k]);
}
if (cur <= X) {
bestCnt = j;
minFuel = cur;
break;
}
}
System.out.println(bestCnt + " " + minFuel);
}
}
python
python
INF = float("inf")
N, X, Y = map(int, input().split())
A = [0] * N
B = [0] * N
for i in range(N):
A[i], B[i] = map(int, input().split())
# dp[j][k] 完成j个运算,使用k个通行费所需最小燃料
dp = [[INF] * (Y + 1) for _ in range(N + 1)]
dp[0][0] = 0
for i in range(N):
a, b = A[i], B[i]
for j in range(N - 1, -1, -1):
for k in range(Y, -1, -1):
if dp[j][k] == INF:
continue
# 常规
dp[j + 1][k] = min(dp[j + 1][k], dp[j][k] + a)
# 冲动
if k + 1 <= Y:
dp[j + 1][k + 1] = min(dp[j + 1][k + 1], dp[j][k] + b)
bestCnt = 0
minFuel = 0
for j in range(N, -1, -1):
cur = min(dp[j])
if cur <= X:
bestCnt = j
minFuel = cur
break
print(bestCnt, minFuel)
Javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on("line", line => {
input.push(line);
});
rl.on("close", () => {
let idx = 0;
const [N, X, Y] = input[idx++].split(" ").map(Number);
const A = new Array(N);
const B = new Array(N);
for (let i = 0; i < N; i++) {
const [a, b] = input[idx++].split(" ").map(Number);
A[i] = a;
B[i] = b;
}
// dp[j][k] 完成j个运算,使用k个通行费所需最小燃料
const dp = Array.from({ length: N + 1 }, () => Array(Y + 1).fill(Number.MAX_SAFE_INTEGER));
dp[0][0] = 0;
for (let i = 0; i < N; i++) {
const a = A[i], b = B[i];
for (let j = N - 1; j >= 0; j--) {
for (let k = Y; k >= 0; k--) {
if (dp[j][k] === Number.MAX_SAFE_INTEGER) {
continue;
}
// 常规
dp[j + 1][k] = Math.min(dp[j + 1][k], dp[j][k] + a);
// 冲动
if (k + 1 <= Y) {
dp[j + 1][k + 1] = Math.min(dp[j + 1][k + 1], dp[j][k] + b);
}
}
}
}
let bestCnt = 0;
let minFuel = 0;
for (let j = N; j >= 0; j--) {
let cur = Number.MAX_SAFE_INTEGER;
for (let k = 0; k <= Y; k++) {
cur = Math.min(cur, dp[j][k]);
}
if (cur <= X) {
bestCnt = j;
minFuel = cur;
break;
}
}
console.log(bestCnt + " " + minFuel);
});
Go
go
package main
import (
"bufio"
"fmt"
"math"
"os"
)
func min(a, b int) int {
if a < b {
return a
}
return b
}
func main() {
in := bufio.NewReader(os.Stdin)
var N, X, Y int
fmt.Fscan(in, &N, &X, &Y)
A := make([]int, N)
B := make([]int, N)
for i := 0; i < N; i++ {
fmt.Fscan(in, &A[i], &B[i])
}
// dp[j][k] 完成j个运算,使用k个通行费所需最小燃料
dp := make([][]int, N+1)
for i := 0; i <= N; i++ {
dp[i] = make([]int, Y+1)
for j := 0; j <= Y; j++ {
dp[i][j] = math.MaxInt32
}
}
dp[0][0] = 0
for i := 0; i < N; i++ {
a, b := A[i], B[i]
for j := N - 1; j >= 0; j-- {
for k := Y; k >= 0; k-- {
if dp[j][k] == math.MaxInt32 {
continue
}
// 常规
dp[j+1][k] = min(dp[j+1][k], dp[j][k]+a)
// 冲动
if k+1 <= Y {
dp[j+1][k+1] = min(dp[j+1][k+1], dp[j][k]+b)
}
}
}
}
bestCnt := 0
minFuel := 0
for j := N; j >= 0; j-- {
cur := math.MaxInt32
for k := 0; k <= Y; k++ {
cur = min(cur, dp[j][k])
}
if cur <= X {
bestCnt = j
minFuel = cur
break
}
}
fmt.Println(bestCnt, minFuel)
}