题目描述
小明需要走路从城市的一端前往另一端。城市可以视为一个长条形,共有 N 个街区,按顺序排成一列,每个街区的右侧紧挨着下一个街区的左侧。初始时,小明位于第 1 个街区的左侧,他的目标是到达第 N 个街区的右侧。步行通过第 n 个街区时,小明需要花费的时间为 a_n。同时,小明可以选择坐最多 M 次地铁。每个街区的左侧都有地铁站,每次坐地铁可以穿越前方最少 1 个,最多 k 个连续的街区。坐地铁穿越任何一个街区所需的时间都是一个常数 B(如果穿越 2 个街区,所需的时间是 2×B,以此类推),进地铁站、出地铁站、等待地铁均不耗费时间。
输入描述
- 第一行包含一个正整数 N,表示街区的数量。
- 第二行包含一个正整数 K,表示每次坐地铁最多可以穿越的街区数量。
- 第三行包含 N 个非负整数,以空格分隔,表示步行穿过每个街区所消耗的时间。
- 第四行包含一个非负整数 B,表示坐地铁穿越一个街区所需的时间。
- 第五行包含一个非负整数 M,表示小明最多可以坐地铁的次数。
数据范围:
- 1 ≤ N ≤ 10
- 1 ≤ K ≤ 10
- 0 ≤ M ≤ 10
- 任何方式通过单个街区所需要的时间(包括所有 a_n 以及 B 的值)不超过 10000。
输出描述
一个整数,表示穿越整个城市花费的最短时间。
用例输入
5
1
3 7 5 3 6
0
2
11
解题思路
问题分析
-
目标:
- 找到小明从第 1 个街区的左侧到第 N 个街区的右侧的最短时间。
- 小明可以选择步行或坐地铁,坐地铁可以穿越多个街区。
-
解题方法:
- 使用动态规划(DP)来解决。
- 定义
dp[i][j]
表示走到第 i 个街区,并且使用了 j 次地铁的最小时间。 - 状态转移方程:
- 不坐地铁只步行:
dp[i][j] = dp[i - 1][j] + times[i]
- 坐地铁的情况:
dp[i][j] = min(dp[i][j], dp[i - l][j - 1] + l * b)
,其中 l 是坐地铁穿越的街区数量(1 ≤ l ≤ k)。
- 不坐地铁只步行:
解题步骤
-
读取输入数据:
- 读取街区数量 N 和每次坐地铁最多可以穿越的街区数量 K。
- 读取步行穿过每个街区所消耗的时间。
- 读取坐地铁穿越一个街区所需的时间 B 和小明最多可以坐地铁的次数 M。
-
初始化 DP 表:
dp[0][0] = 0
,表示初始时小明在第 1 个街区的左侧,且未使用地铁。- 其他状态初始化为一个较大的值(如
INT_MAX / 2
)。
-
状态转移:
- 对于每个街区 i(1 ≤ i ≤ N):
- 不坐地铁只步行:
dp[i][j] = dp[i - 1][j] + times[i]
- 坐地铁的情况:枚举从前面哪个街区开始坐地铁,更新
dp[i][j]
。
- 不坐地铁只步行:
- 对于每个街区 i(1 ≤ i ≤ N):
-
统计结果:
- 遍历所有可能的地铁使用次数,找到最小的时间。
代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
#include<sstream>
#include<bitset>
#include<stack>
#include<climits>
#include<iomanip>
#include<cstdint>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<int> times(n + 1);
for (int i = 1; i <= n; i++) {
cin >> times[i];
}
int b, m;
cin >> b >> m;
// dp[i][j]:表示走到第 i 个街区,并且使用了 j 次地铁的最小时间。
vector<vector<int>> dp(n + 1, vector<int>(m + 1, INT_MAX / 2));
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
// 不坐地铁只步行
dp[i][0] = dp[i - 1][0] + times[i];
// 坐地铁的情况
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j] + times[i];
// 枚举从前面哪里开始坐
for (int l = 1; l <= k; l++) {
if (i - l <= 0) {
dp[i][j] = min(dp[i][j], dp[0][j - 1] + i * b);
} else {
dp[i][j] = min(dp[i][j], dp[i - l][j - 1] + l * b);
}
}
}
}
int res = INT_MAX;
for (int i = 0; i <= m; i++) {
res = min(res, dp[n][i]);
}
cout << res;
}