机试题——城市穿越问题

题目描述

小明需要走路从城市的一端前往另一端。城市可以视为一个长条形,共有 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. 目标

    • 找到小明从第 1 个街区的左侧到第 N 个街区的右侧的最短时间。
    • 小明可以选择步行或坐地铁,坐地铁可以穿越多个街区。
  2. 解题方法

    • 使用动态规划(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)。

解题步骤

  1. 读取输入数据

    • 读取街区数量 N 和每次坐地铁最多可以穿越的街区数量 K。
    • 读取步行穿过每个街区所消耗的时间。
    • 读取坐地铁穿越一个街区所需的时间 B 和小明最多可以坐地铁的次数 M。
  2. 初始化 DP 表

    • dp[0][0] = 0,表示初始时小明在第 1 个街区的左侧,且未使用地铁。
    • 其他状态初始化为一个较大的值(如 INT_MAX / 2)。
  3. 状态转移

    • 对于每个街区 i(1 ≤ i ≤ N):
      • 不坐地铁只步行:dp[i][j] = dp[i - 1][j] + times[i]
      • 坐地铁的情况:枚举从前面哪个街区开始坐地铁,更新 dp[i][j]
  4. 统计结果

    • 遍历所有可能的地铁使用次数,找到最小的时间。

代码实现

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值