题解 | #星际勘探者#

星际勘探者

https://www.nowcoder.com/practice/1a9c4a33372443a9944cd458a3269dac

题目链接

星际勘探者

题目描述

模拟一艘星际勘探舰穿越由 m 颗小行星组成的直线路径。舰船从一个初始能量 n 开始,每航行到下一颗小行星固定消耗能量 1。在整个任务中,最多可以使用采集器 k 次,采集第 i 颗小行星可以补充能量 a[i]。目标是计算在整个任务过程中的任意时刻,舰船所能达到的最大能量值。

  • 约束条件:
    • 只能沿小行星编号递增的方向前进。
    • 航行前能量必须大于 0。
    • 每颗小行星最多采集一次。

解题思路

这是一个典型的动态规划问题,可以看作是 0/1 背包问题的一个变种。我们需要在遍历小行星的过程中,对每个小行星做决策(采集或不采集),并维护在不同采集次数下的最大能量状态。

  1. DP 状态定义

    • 我们可以使用一维 DP 数组来优化空间复杂度。
    • 定义 dp[j] 为:在处理完当前考察的小行星后,总共使用了 j 次采集器所能达到的最大剩余能量。数组大小为 k+1
  2. DP 状态转移

    • 我们按顺序遍历每一颗小行星 a[i]
    • 对于每颗小行星,我们需要根据上一个阶段dp 状态,计算出当前阶段的新 dp 状态。为避免状态更新的相互干扰,我们可以使用一个临时数组 temp_dp 来存储新状态。
    • 遍历所有可能的采集次数 j (从 0k):
      • temp_dp[j] 的值由两种可能的前置状态共同决定:
        1. 路径一:不采集当前小行星 a[i]
          • 这个决策必须基于上一个阶段已经采集了 j 次的状态,即 dp[j]
          • 前提条件是 dp[j] > 0 (有足够能量航行)。
          • 如果满足,则航行后能量为 dp[j] - 1
        2. 路径二:采集当前小行星 a[i]
          • 这个决策必须基于上一个阶段已经采集了 j-1 次的状态,即 dp[j-1]
          • 前提条件是 j > 0 (有采集次数) 且 dp[j-1] > 0 (有足够能量航行)。
          • 如果满足,则航行并采集后能量为 dp[j-1] - 1 + a[i]
      • 新的 temp_dp[j] 就是这两条路径所能达到的能量的最大值。如果两条路径都不可行,则该状态不可达(值为-1)。
    • 在计算完当前小行星的所有新状态后,将 temp_dp 数组的内容复制回 dp 数组,为下一轮迭代做准备。
  3. 追踪最大能量

    • 题目要求的是过程中的最大能量,而不仅仅是最终能量。
    • 因此,需要一个全局变量 max_overall_energy,初始化为 n
    • 在每次计算出一个有效的 temp_dp[j] 值后,都需要用它来更新 max_overall_energy
  4. 初始化

    • dp 数组大小为 k+1
    • dp[0] = n:表示初始状态,0 次采集,能量为 n
    • dp[1...k] 初始化为无效值(如 -1),表示这些状态尚未达到。

代码

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int m, n, k;
    cin >> m >> n >> k;

    vector<int> a(m);
    for (int i = 0; i < m; ++i) {
        cin >> a[i];
    }

    vector<long long> dp(k + 1, -1);
    dp[0] = n;

    long long max_overall_energy = n;

    for (int i = 0; i < m; ++i) { // 遍历每颗小行星 a[i]
        vector<long long> temp_dp = dp; // 使用临时数组防止状态干扰

        for (int j = 0; j <= k; ++j) {
            // 来源1: 不采集小行星 a[i]。
            // 从上一步的 dp[j] 状态转移而来。
            long long energy_from_no_collect = -1;
            if (dp[j] > 0) {
                energy_from_no_collect = dp[j] - 1;
            }

            // 来源2: 采集小行星 a[i]。
            // 从上一步的 dp[j-1] 状态转移而来。
            long long energy_from_collect = -1;
            if (j > 0 && dp[j - 1] > 0) {
                energy_from_collect = dp[j - 1] - 1 + a[i];
            }

            temp_dp[j] = max(energy_from_no_collect, energy_from_collect);
            max_overall_energy = max(max_overall_energy, temp_dp[j]);
        }
        dp = temp_dp;
    }

    cout << max_overall_energy << endl;

    return 0;
}
import java.util.Scanner;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int m = sc.nextInt();
        int n = sc.nextInt();
        int k = sc.nextInt();

        int[] a = new int[m];
        for (int i = 0; i < m; i++) {
            a[i] = sc.nextInt();
        }

        long[] dp = new long[k + 1];
        Arrays.fill(dp, -1);
        dp[0] = n;

        long maxOverallEnergy = n;

        for (int i = 0; i < m; i++) { // 遍历每颗小行星 a[i]
            long[] tempDp = Arrays.copyOf(dp, dp.length);

            for (int j = 0; j <= k; j++) {
                // 来源1: 不采集小行星 a[i]
                long energyFromNoCollect = -1;
                if (dp[j] > 0) {
                    energyFromNoCollect = dp[j] - 1;
                }

                // 来源2: 采集小行星 a[i]
                long energyFromCollect = -1;
                if (j > 0 && dp[j - 1] > 0) {
                    energyFromCollect = dp[j - 1] - 1 + a[i];
                }

                tempDp[j] = Math.max(energyFromNoCollect, energyFromCollect);
                maxOverallEnergy = Math.max(maxOverallEnergy, tempDp[j]);
            }
            dp = tempDp;
        }

        System.out.println(maxOverallEnergy);
    }
}
def main():
    m, n, k = map(int, input().split())
    a = list(map(int, input().split()))

    # dp[j] 表示在当前步骤后,采集了j次的最大能量
    dp = [-1] * (k + 1)
    dp[0] = n

    max_overall_energy = n

    for i in range(m): # 遍历每颗小行星 a[i]
        temp_dp = list(dp)

        for j in range(k + 1):
            # 来源1: 不采集小行星 a[i]
            energy_from_no_collect = -1
            if dp[j] > 0:
                energy_from_no_collect = dp[j] - 1

            # 来源2: 采集小行星 a[i]
            energy_from_collect = -1
            if j > 0 and dp[j - 1] > 0:
                energy_from_collect = dp[j - 1] - 1 + a[i]
            
            # 新的 dp[j] 是两种来源中的最大值
            temp_dp[j] = max(energy_from_no_collect, energy_from_collect)
            max_overall_energy = max(max_overall_energy, temp_dp[j])
        
        dp = temp_dp

    print(max_overall_energy)

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法: 动态规划 (0/1 背包变种)
  • 时间复杂度: 。其中 m 是小行星的数量, k 是最大采集次数。外层循环遍历 m 颗小行星,内层循环遍历 k+1 种采集状态。
  • 空间复杂度: 。我们使用了大小为 k+1 的一维数组来存储 DP 状态。
全部评论

相关推荐

09-12 11:55
已编辑
湖南工商大学 Java
那一天的Java_J...:这种一堆问题的,别去
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务