题解 | #中世纪的商路#

中世纪的商路

https://www.nowcoder.com/practice/62fcb41a02aa42798c856c17bef25776

题目链接

中世纪的商路

题目描述

给定 N 个贸易机会,每个机会有出发时间、到达时间、所需商队规模和预期利润。在任何时刻,只能派遣一支商队,因此不能同时进行时间重叠的贸易活动(一个在 t 时刻结束的活动与一个在 t 时刻开始的活动不算重叠)。

目标是在不超过最大商队规模 C 的前提下,选择一个不重叠的贸易活动子集,使得总利润最大化。

解题思路

这是一个经典的“加权区间调度”(Weighted Interval Scheduling)问题,是动态规划的典型应用。

  1. 预处理

    • 数据结构化:将输入的四个数组 start, end, fleet, profit 合并为一个对象/结构体列表,每个对象代表一个贸易机会。
    • 过滤:遍历所有贸易机会,剔除那些所需商队规模大于最大限制 C 的机会。
    • 排序:这是解决此类问题的核心步骤。将所有可行的贸易机会按结束时间 end 升序排序。这个排序确保了当我们考虑第 i 个机会时,所有可能在它之前完成的机会都已经位于 0i-1 的位置,这为动态规划提供了最优子结构。
  2. 动态规划

    • DP 状态定义

      • dp[i] 表示在考虑前 i+1 个(即索引从 0i 的)贸易机会时,所能获得的最大总利润。这个定义允许不选择第 i 个机会。
    • DP 状态转移

      • 我们从 i = 0N-1 遍历所有排序后的贸易机会。对于第 i 个机会,我们有两种选择:
        1. 不执行第 i 个机会
          • 这种情况下,最大利润与只考虑前 i 个机会(即索引 0i-1)时是相同的。
          • 利润为 dp[i-1] (如果 i > 0 的话)。
        2. 执行第 i 个机会
          • 我们首先获得第 i 个机会自身的利润 profit[i]
          • 为了最大化总利润,我们还需要加上在第 i 个机会开始之前所能积累的最大利润。这意味着我们需要找到一个与第 i 个机会不冲突的、最优的先前活动序列。
          • 具体地,我们需要找到最后一个结束时间不晚于第 i 个机会开始时间 (end_time[p] <= start_time[i]) 的机会 p
          • 由于列表是按结束时间排序的,我们可以通过二分查找0i-1 的范围内高效地找到这个 p
          • 如果找到了这样的 p,那么执行第 i 个机会的总利润就是 profit[i] + dp[p]。如果找不到,则总利润就是 profit[i]
    • 递推公式

      • dp[i] = max( 不执行 i 的利润, 执行 i 的利润 )
      • dp[i] = max(dp[i-1], profit[i] + (dp[p] 或 0))
  3. 最终结果

    • dp 数组的最后一个元素 dp[N-1] 即为所求的最大总利润。

这种 “排序 + DP + 二分查找” 的方法,将时间复杂度从朴素的 优化到了

代码

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <map>

using namespace std;

struct Trade {
    int start;
    int end;
    int fleet;
    int profit;
};

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

    string line;
    vector<int> starts, ends, fleets, profits;
    int val;

    getline(cin, line); stringstream ss_starts(line);
    while (ss_starts >> val) starts.push_back(val);

    getline(cin, line); stringstream ss_ends(line);
    while (ss_ends >> val) ends.push_back(val);

    getline(cin, line); stringstream ss_fleets(line);
    while (ss_fleets >> val) fleets.push_back(val);

    getline(cin, line); stringstream ss_profits(line);
    while (ss_profits >> val) profits.push_back(val);

    int max_fleet;
    cin >> max_fleet;

    map<int, vector<Trade>> trades_ending_at;
    int max_time = 0;
    for (size_t i = 0; i < starts.size(); ++i) {
        if (fleets[i] <= max_fleet) {
            trades_ending_at[ends[i]].push_back({starts[i], ends[i], fleets[i], profits[i]});
            max_time = max(max_time, ends[i]);
        }
    }

    vector<vector<long long>> dp(max_time + 1, vector<long long>(max_fleet + 1, 0));

    for (int t = 1; t <= max_time; ++t) {
        for (int w = 0; w <= max_fleet; ++w) {
            dp[t][w] = dp[t - 1][w];
        }

        if (trades_ending_at.count(t)) {
            for (const auto& trade : trades_ending_at[t]) {
                int start_time = trade.start;
                int fleet_cost = trade.fleet;
                int profit_val = trade.profit;

                for (int w = max_fleet; w >= fleet_cost; --w) {
                    dp[t][w] = max(dp[t][w], dp[start_time][w - fleet_cost] + profit_val);
                }
            }
        }
    }

    long long max_profit = 0;
    for (int w = 0; w <= max_fleet; ++w) {
        max_profit = max(max_profit, dp[max_time][w]);
    }

    cout << max_profit << endl;

    return 0;
}

import java.util.*;

class Trade {
    int start, end, fleet, profit;

    public Trade(int start, int end, int fleet, int profit) {
        this.start = start;
        this.end = end;
        this.fleet = fleet;
        this.profit = profit;
    }
}

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

        String[] startStrs = sc.nextLine().split(" ");
        String[] endStrs = sc.nextLine().split(" ");
        String[] fleetStrs = sc.nextLine().split(" ");
        String[] profitStrs = sc.nextLine().split(" ");
        int maxFleet = Integer.parseInt(sc.nextLine());

        Map<Integer, List<Trade>> tradesEndingAt = new HashMap<>();
        int maxTime = 0;

        for (int i = 0; i < startStrs.length; i++) {
            if (startStrs[i].isEmpty()) continue;
            int fleet = Integer.parseInt(fleetStrs[i]);
            if (fleet <= maxFleet) {
                int start = Integer.parseInt(startStrs[i]);
                int end = Integer.parseInt(endStrs[i]);
                int profit = Integer.parseInt(profitStrs[i]);
                
                tradesEndingAt.putIfAbsent(end, new ArrayList<>());
                tradesEndingAt.get(end).add(new Trade(start, end, fleet, profit));
                maxTime = Math.max(maxTime, end);
            }
        }

        long[][] dp = new long[maxTime + 1][maxFleet + 1];

        for (int t = 1; t <= maxTime; t++) {
            for (int w = 0; w <= maxFleet; w++) {
                dp[t][w] = dp[t - 1][w];
            }

            if (tradesEndingAt.containsKey(t)) {
                for (Trade trade : tradesEndingAt.get(t)) {
                    int startTime = trade.start;
                    int fleetCost = trade.fleet;
                    int profitVal = trade.profit;

                    for (int w = maxFleet; w >= fleetCost; w--) {
                        dp[t][w] = Math.max(dp[t][w], dp[startTime][w - fleetCost] + profitVal);
                    }
                }
            }
        }

        long maxProfit = 0;
        for (int w = 0; w <= maxFleet; w++) {
            maxProfit = Math.max(maxProfit, dp[maxTime][w]);
        }

        System.out.println(maxProfit);
    }
}

def solve():
    starts = list(map(int, input().split()))
    ends = list(map(int, input().split()))
    fleets = list(map(int, input().split()))
    profits = list(map(int, input().split()))
    max_fleet = int(input())

    trades_ending_at = {}
    max_time = 0
    for i in range(len(starts)):
        if fleets[i] <= max_fleet:
            end_time = ends[i]
            if end_time not in trades_ending_at:
                trades_ending_at[end_time] = []
            trades_ending_at[end_time].append((starts[i], fleets[i], profits[i]))
            max_time = max(max_time, end_time)

    if max_time == 0:
        print(0)
        return

    dp = [[0] * (max_fleet + 1) for _ in range(max_time + 1)]

    for t in range(1, max_time + 1):
        for w in range(max_fleet + 1):
            dp[t][w] = dp[t - 1][w]

        if t in trades_ending_at:
            for start_time, fleet_cost, profit_val in trades_ending_at[t]:
                for w in range(max_fleet, fleet_cost - 1, -1):
                    dp[t][w] = max(dp[t][w], dp[start_time][w - fleet_cost] + profit_val)
    
    max_profit = 0
    if max_time > 0:
        max_profit = max(dp[max_time])

    print(max_profit)

solve()

算法及复杂度

  • 算法: 动态规划 (基于时间轴的背包问题)
  • 时间复杂度:
    • 是最大结束时间, 是最大商队规模, 是有效贸易机会数量。
    • 初始化 DP 表和时间轴遍历是
    • 处理所有贸易机会,每个机会都需要一次内层背包循环,总共是
    • 总复杂度由这两部分构成。
  • 空间复杂度:
    • 主要用于存储 状态表。
全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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