新年即将来临,小明计划开新买的电动汽车回老家过年。
已知小明的工作地在上海,老家在中部某城市A。上海到城市A的距离是L
公里(1 <= L <= 100000)
。
小明的电动汽车的电池航程是P (1 <= P <= 100)
,电池最大电量也是P
(假设电动汽车行驶一公里需要消耗1度电)。如果电动车在中途电量耗尽了,将无法继续前行,也就无法到达目的地了。
已知小明出发前已经把电池充满了。途中依次经过N (1 <= N < 10000)
个充电站。
第i
个充电站距离城市A(终点)有Ai
公里,最大可充电Bi
度。
请问,小明能不能顺利地回老家过年?如果可以,请输出最少需要充电多少次;如果不可以,请输出-1
。
输入的第一行为数字N
。
接下来的N
行,每行包含2
个数并用空格隔开,分别表示Ai Bi
最后一行包括两个数L P
,并用空格隔开。
按照题目要求输出最少次数或者-1
。
4
4 4
5 5
11 6
15 8
25 10
3
小明出发时电池电量是10
,距离城市A 25
公里。
行驶10
公里后,到达距离城市A 15
公里的充电站,充电前电池电量为0
,充电8
度之后,再出发。
行驶4
公里后,到达距离城市A 11
公里的充电站,充电前电池电量为4
。充电6
度之后,再出发。
行驶6
公里后,到达距离城市A 5
公里的充电站,充电前电池电量为4
,充电1
度之后,再出发。
之后,可以直接开到城市A一共需要充电3
次才能到达城市A。
时间限制: C/C++ 1000ms
, 其他语言2000ms
内存****限制: C/C++ 256MB
,其他语言 512MB
每一个充电站都有选和不选两种状态,所以我们可以把题目看作是路径无关、求最少选择数的01背包。
dp
数组的含义是什么?dp
数组是一个长度为(n+1)*(P+1)
的二维矩阵,dp[i][j]
表示来到第i
个充电桩时,剩余电量为j
,所需要的最少充电次数。dp[n]
表示来到A城的结果。来到第i
个充电桩时,假设剩余电量为j
,距离下一个充电桩的距离为 A i + 1 − A i A_{i+1}-A_{i} Ai+1−Ai,即花费的电量为diff = a_list[i+1]-a_list[i]
。如果
min(j+b_list[i], P)
,即充电后电量不能超过最大电量。
i+1
个充电桩时,剩余电量为nxt = min(j+b_list[i], P) - diff
dp[i+1][nxt] = min(dp[i][j]+1, dp[i+1][nxt])
+1
表示在第i
个充电桩处进行了多一次充电i+1
个充电桩时,剩余电量为nxt = j - diff
dp[i+1][nxt] = min(dp[i][j], dp[i+1][nxt])
nxt >= 0
,即从第i个充电桩开往第i+1个充电桩时,剩余电量不能够耗尽。for i in range(0, N):
a, b = lst[i]
for j in range(0, P+1):
if dp[i][j] == inf:
continue
else:
diff = lst[i+1][0] - lst[i][0]
nxt = min(j+b, P) - diff
if nxt >= 0:
dp[i+1][nxt] = min(dp[i][j]+1, dp[i+1][nxt])
nxt = j-diff
if nxt >= 0:
dp[i+1][nxt] = min(dp[i][j], dp[i+1][nxt])
dp
数组如何初始化?0
个充电站时,剩余电量为P-a_list[0]
,此时最少充电方法数为0
次。故初始化dp[0][P-a_list[0]] = 0
。对于其他位置,初始化为inf
dp = [[inf] * (P+1) for _ in range(N+1)]
dp[0][P-a_list[0]] = 0
考虑完上述问题后,代码其实呼之欲出了。
# 题目:【DP】华为2023秋招-开电动汽车回家过年
# 作者:闭着眼睛学数理化
# 算法:DP
# 代码有看不懂的地方请直接在群上提问
from math import inf
# 充电站数目
N = int(input())
# 储存充电站位置、最大充电量的列表lst
lst = list()
for _ in range(N):
a, b = map(int, input().split())
lst.append((a, b))
# 终点的距离L,电池最大容量P
L, P = map(int, input().split())
# 实际有用的距离是充电站到起点的距离L-a
# 得到结果后根据距离起点的远近进行升序排序
# 注意,lst最后一个位置还储存了终点的情况,即离起点的距离为L
lst = sorted((L-a, b) for a, b in lst) + [(L, 0)]
# dp数组初始化为一个长度为(n+1)*(P+1)的二维矩阵
# dp[i][j]表示来到第i个充电桩时,剩余电量为j,
# 所需要的最少充电次数
dp = [[inf] * (P+1) for _ in range(N+1)]
# 如果第0个充电站的位置,从起点无法到达,则直接输出-1
if lst[0][0] > P:
print(-1)
# 否则,记录到达第0个充电站,且剩余电量为P-lst[0][0]
# 的最少充电次数为0
else:
dp[0][P - lst[0][0]] = 0
# 遍历所有充电站
for i in range(0, N):
# 第i个充电站到起点的距离a,最大可充电数b
a, b = lst[i]
# 遍历所有可能的电量j
for j in range(0, P+1):
# 遇到inf,说明没有任何一种方式
# 使得到达第i个充电站时,剩余电量j
if dp[i][j] == inf:
continue
# 否则,考虑从第i个充电站出发
# 充电或不充电,到达第i+1个充电站的情况
else:
# 两个充电站之间的距离
diff = lst[i+1][0] - lst[i][0]
# 考虑在第i个充电站充电的情况
# 充电之后,不能超过最大电量
nxt = min(j+b, P) - diff
if nxt >= 0:
dp[i+1][nxt] = min(dp[i][j]+1, dp[i+1][nxt])
# 考虑不在第i个充电站充电的情况
nxt = j-diff
if nxt >= 0:
dp[i+1][nxt] = min(dp[i][j], dp[i+1][nxt])
# dp[N]表示到达终点的情况
ans = min(dp[N])
print(ans if ans != inf else -1)
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int[][] lst = new int[N][2];
for (int i = 0; i < N; i++) {
lst[i][0] = sc.nextInt();
lst[i][1] = sc.nextInt();
}
int L = sc.nextInt();
int P = sc.nextInt();
int[][] sortedLst = new int[N + 1][2];
for (int i = 0; i < N; i++) {
sortedLst[i][0] = L - lst[i][0];
sortedLst[i][1] = lst[i][1];
}
sortedLst[N][0] = L;
sortedLst[N][1] = 0;
Arrays.sort(sortedLst, (a, b) -> Integer.compare(a[0], b[0]));
int[][] dp = new int[N + 1][P + 1];
for (int i = 0; i <= N; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
if (sortedLst[0][0] > P) {
System.out.println(-1);
} else {
dp[0][P - sortedLst[0][0]] = 0;
for (int i = 0; i < N; i++) {
int a = sortedLst[i][0];
int b = sortedLst[i][1];
for (int j = 0; j <= P; j++) {
if (dp[i][j] == Integer.MAX_VALUE) {
continue;
}
int diff = sortedLst[i + 1][0] - a;
int nxt = Math.min(j + b, P) - diff;
if (nxt >= 0) {
dp[i + 1][nxt] = Math.min(dp[i][j] + 1, dp[i + 1][nxt]);
}
nxt = j - diff;
if (nxt >= 0) {
dp[i + 1][nxt] = Math.min(dp[i][j], dp[i + 1][nxt]);
}
}
}
int ans = Arrays.stream(dp[N]).min().orElse(Integer.MAX_VALUE);
System.out.println(ans != Integer.MAX_VALUE ? ans : -1);
}
}
}
#include
#include
#include
#include
using namespace std;
int main() {
int N;
cin >> N;
vector> lst(N, vector(2));
for (int i = 0; i < N; i++) {
cin >> lst[i][0] >> lst[i][1];
}
int L, P;
cin >> L >> P;
vector> sortedLst(N + 1, vector(2));
for (int i = 0; i < N; i++) {
sortedLst[i][0] = L - lst[i][0];
sortedLst[i][1] = lst[i][1];
}
sortedLst[N][0] = L;
sortedLst[N][1] = 0;
sort(sortedLst.begin(), sortedLst.end(), [](const vector& a, const vector& b) {
return a[0] < b[0];
});
vector> dp(N + 1, vector(P + 1, INT_MAX));
if (sortedLst[0][0] > P) {
cout << -1 << endl;
} else {
dp[0][P - sortedLst[0][0]] = 0;
for (int i = 0; i < N; i++) {
int a = sortedLst[i][0];
int b = sortedLst[i][1];
for (int j = 0; j <= P; j++) {
if (dp[i][j] == INT_MAX) {
continue;
}
int diff = sortedLst[i + 1][0] - a;
int nxt = min(j + b, P) - diff;
if (nxt >= 0) {
dp[i + 1][nxt] = min(dp[i][j] + 1, dp[i + 1][nxt]);
}
nxt = j - diff;
if (nxt >= 0) {
dp[i + 1][nxt] = min(dp[i][j], dp[i + 1][nxt]);
}
}
}
int ans = *min_element(dp[N].begin(), dp[N].end());
cout << (ans != INT_MAX ? ans : -1) << endl;
}
return 0;
}
时间复杂度:O(NP)
。dp过程需要进行双重循环遍历。
空间复杂度:O(NP)
。二维dp数组所占空间。
华为OD算法冲刺训练
华为OD算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!
课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!
30+天陪伴式学习,20+直播课时,300+动画图解视频,200+LeetCode经典题,100+华为OD真题,还有简历修改与模拟面试将为你解锁
可查看链接 OD算法冲刺训练课程表 & OD真题汇总(持续更新)
绿色聊天软件戳 od1336了解更多