#P2943. 第3题-忍者
-
1000ms
Tried: 39
Accepted: 11
Difficulty: 5
所属公司 :
京东
时间 :2025年5月10日
-
算法标签>动态规划
第3题-忍者
解题思路
本题是一个典型的动态规划问题。忍者的目标是从第一排或第二排的房间 1 出发,到达第一排或第二排的房间 n,要求最小化消耗的时间。根据题意,我们可以从每个房间选择不同的路径(在两个房间之间跳跃或在同一排内移动),每种路径都有不同的消耗时间。我们需要通过动态规划来计算从起点到终点的最短时间。
动态规划状态定义
我们可以定义动态规划的状态如下:
dp1[i]表示从第一排的房间 i 出发,到达第 i 个房间所需的最小时间。dp2[i]表示从第二排的房间 i 出发,到达第 i 个房间所需的最小时间。
接下来,我们根据题目给出的条件,来递推计算每个房间的最小花费。
状态转移方程
1. 同一排内的转移
- 从第一排房间 i 到房间 i+1 的消耗时间为
|a[i] - a[i+1]|。 - 从第二排房间 i 到房间 i+1 的消耗时间为
|b[i] - b[i+1]|。
2. 跨排的转移
- 从第一排房间 i 到第二排房间 i 的消耗时间为
2 * |a[i] - b[i]|。 - 从第二排房间 i 到第一排房间 i 的消耗时间为
2 * |b[i] - a[i]|。 - 从第一排房间 i 到第二排房间 i+1 的消耗时间为
3 * |a[i] - b[i+1]|。 - 从第二排房间 i 到第一排房间 i+1 的消耗时间为
3 * |b[i] - a[i+1]|。
初始状态
- 起点可以是第一排或第二排的房间 1。因此:
dp1[1] = 0dp2[1] = 0
递推计算
从房间 1 出发,逐步计算每个房间的最小时间,最终得到到达房间 n 的最小时间。
时间复杂度分析
每个状态的计算只依赖于前一个状态,因此动态规划的复杂度是 O(n),其中 n 是房间的数量。
代码实现
Python 代码
def main():
n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
dp1 = [0] * n
dp2 = [0] * n
dp1[0] = 0
dp2[0] = 0
for i in range(1, n):
dp1[i] = min(dp1[i - 1] + abs(a[i] - a[i - 1]),
dp2[i - 1] + 3 * abs(b[i - 1] - a[i]))
dp2[i] = min(dp2[i - 1] + abs(b[i] - b[i - 1]),
dp1[i - 1] + 3 * abs(a[i - 1] - b[i]))
dp1[i] = min(dp1[i], dp2[i] + 2 * abs(a[i] - b[i]))
dp2[i] = min(dp2[i], dp1[i] + 2 * abs(a[i] - b[i]))
print(min(dp1[n - 1], dp2[n - 1]))
if __name__ == "__main__":
main()
Java 代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
int[] b = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
for (int i = 0; i < n; i++) {
b[i] = sc.nextInt();
}
long[] dp1 = new long[n];
long[] dp2 = new long[n];
dp1[0] = 0;
dp2[0] = 0;
for (int i = 1; i < n; i++) {
dp1[i] = Math.min(dp1[i - 1] + Math.abs(a[i] - a[i - 1]),
dp2[i - 1] + 3 * (long)Math.abs(b[i - 1] - a[i]));
dp2[i] = Math.min(dp2[i - 1] + Math.abs(b[i] - b[i - 1]),
dp1[i - 1] + 3 * (long)Math.abs(a[i - 1] - b[i]));
dp1[i] = Math.min(dp1[i], dp2[i] + 2 * (long)Math.abs(a[i] - b[i]));
dp2[i] = Math.min(dp2[i], dp1[i] + 2 * (long)Math.abs(a[i] - b[i]));
}
System.out.println(Math.min(dp1[n - 1], dp2[n - 1]));
sc.close();
}
}
C++ 代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n), b(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
}
// 动态规划数组,使用 long long 防止溢出
vector<long long> dp1(n), dp2(n);
// 初始化
dp1[0] = 0;
dp2[0] = 0;
// 动态规划转移
for (int i = 1; i < n; i++) {
dp1[i] = min({dp1[i - 1] + abs(a[i] - a[i - 1]),
dp2[i - 1] + 3LL * abs(b[i - 1] - a[i])
}); // 乘以 3 使用 long long
dp2[i] = min({dp2[i - 1] + abs(b[i] - b[i - 1]),
dp1[i - 1] + 3LL * abs(a[i - 1] - b[i])
}); // 乘以 3 使用 long long
dp1[i] = min(dp1[i], dp2[i] + 2LL * abs(a[i] - b[i]));
dp2[i] = min(dp2[i], dp1[i] + 2LL * abs(a[i] - b[i]));
}
// 输出最小时间
cout << min(dp1[n - 1], dp2[n - 1]) << endl;
return 0;
}
题目内容
忍者在练习飞檐走壁。现在有两排房子。从左往右排列各有 n 间。如下图。在每个房间的房顶。他有 6 个选择:
第一排:1 2 3 ... n
第二排:1 2 3 ... n
1:从第一排房间 i 到第一排的房间 i+1 。消耗时间为两个房间的高度差。
2:从第二排房间 i 到第二排的房间 i+1 。消耗时间为两个房间的高度差。
3:从第一排房间 i 到第二排的房间 i 。消耗时间为两个房间的高度差 ∗ 2 。
4:从第二排房间 i 到第一排的房间 i 。消耗时间为两个房间的高度差 ∗ 2 。
5:从第一排房间 i 到第二排的房间 i+1 。消耗时间为两个房间的高度差 ∗ 3 。
6:从第二排房间 i 到第一排的房间 i+1 。消耗时间为两个房间的高度差 ∗ 3 。
注意:高度差应该是两个房间的高度的差的绝对值。
他希望花费最少的时间从第一排或第二排的房间 1 开始,到达第一排或者第二排的房间 n 。
请你求一下需要花费最少的时间是多少。
输入描述
一行:n (每排的房子数,1<=n<=105)
一行:a[1],a[2],...,a[n](1<=a[i]<=109)
一行:b[1],b[2],...,b[n](1<=b[i]<=109)
输出描述
一个整数。需要花费的最少的时间。
样例1
输入
5
1 10 20 30 40
2 5 20 50 31
输出
31