青训营X豆包MarsCode 代码详解 day6 | 豆包MarsCode AI 刷题

73 阅读8分钟

环形数组中的最大贡献

问题描述

小S拿到了一个长度为 nn 的环形数组,并定义了两个下标 ii 和 jj 的贡献值公式为:
f(i, j) = (a_i + a_j) × dist(i, j)
其中 dist(i, j) 是下标 ii 和 jj 在数组中的最短距离。小S希望找到一对下标,使得它们的贡献值尽可能大。环形数组的特点是最左和最右的元素也是相邻的。你需要帮助她找到最大贡献值。

例如,给定数组 [1, 2, 3],由于是环形数组,任意两个下标的距离都是1,因此 f(2,3)=(2+3)×1=5f(2,3)=(2+3)×1=5。


测试样例

样例1:

输入:n = 3,a = [1, 2, 3]
输出:5

样例2:

输入:n = 4,a = [4, 1, 2, 3]
输出:12

样例3:

输入:n = 5,a = [1, 5, 3, 7, 2]
输出:24

暴力破解

题目要求在一个环形数组中找到一对下标 i 和 j,使得它们的贡献值 f(i, j) = (a_i + a_j) × dist(i, j) 最大。其中 dist(i, j) 是下标 i 和 j 在数组中的最短距离。由于数组是环形的,最左和最右的元素也是相邻的。

数据结构选择

  • 使用一个整数数组 a 来存储输入的环形数组。
  • 使用两个嵌套的循环来遍历所有可能的下标对 (i, j)

算法步骤

  1. 初始化

    • 定义一个变量 max 来存储当前找到的最大贡献值,初始值为 Integer.MIN_VALUE
    • 定义一个变量 dis 来存储当前下标对 (i, j) 的距离。
  2. 遍历所有下标对

    • 使用两个嵌套的 for 循环遍历所有可能的下标对 (i, j)

    • 计算下标 i 和 j 之间的距离 dis

      • 如果 Math.abs(i - j) 大于 n / 2,则 dis 为 n - Math.abs(i - j)
      • 否则,dis 为 Math.abs(i - j)
    • 计算当前下标对 (i, j) 的贡献值 (a[i] + a[j]) * dis

    • 如果当前贡献值大于 max,则更新 max

  3. 返回结果

    • 返回 max 作为最终的最大贡献值。
    public static int solution(int n, int[] a) {
        // write code here
        int dis=0;
        int max=Integer.MIN_VALUE;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if (Math.abs(i-j)>n/2) {
                    dis=n-Math.abs(i-j);
                }else{
                    dis=Math.abs(i-j);
                }
                if ((a[i]+a[j])*dis>max) {
                    max=(a[i]+a[j])*dis;
                }
            }
        }
        return max; // Placeholder return
    }

    public static void main(String[] args) {
        System.out.println(solution(3, new int[]{1, 2, 3}) == 5);
        System.out.println(solution(4, new int[]{4, 1, 2, 3}) == 12);
        System.out.println(solution(5, new int[]{1, 5, 3, 7, 2}) == 24);
    }
}

优化青海湖至景点X的路线租车成本

问题描述

小F计划从青海湖出发,前往一个遥远的景点X进行旅游。景点X可能是“敦煌”或“月牙泉”,线路的路径是唯一的。由于油价的不断上涨,小F希望尽量减少行程中的燃油成本。车辆的油箱容量为400L,在起始点租车时,车内剩余油量为 200L。每行驶 1km 消耗 1L 油。沿途设有多个加油站,小F可以在这些加油站补充燃油;此外,到达目标景点X还车的时候,需要保证车内剩余的油至少有 200L。

小F需要你帮助他计算,如果合理规划加油站的加油顺序和数量,最小化从青海湖到景点X的旅行成本(元)。

输入:

  • distance:从青海湖到景点X的总距离(km),距离最远不超过 10000 km。
  • n:沿途加油站的数量 (1 <= n <= 100)
  • gas_stations:每个加油站的信息,包含两个非负整数 [加油站距离起始点的距离(km), 该加油站的油价(元/L)]

输出:

  • 最小化从青海湖到景点X的旅行成本(元)。如果无法到达景点X,或者到达景点X还车时油料剩余不足 200L,则需要返回 -1 告诉小F这是不可能的任务。

测试样例

样例1:

输入:distance = 500, n = 4, gas_stations = [[100, 1], [200, 30], [400, 40], [300, 20]]
输出:4300

样例2:

输入:distance = 1000, n = 3, gas_stations = [[300, 25], [600, 35], [900, 5]]
输出:-1

样例3:

输入:distance = 200, n = 2, gas_stations = [[100, 50], [150, 45]]
输出:9000

样例4:

输入:distance = 700, n = 5, gas_stations = [[100, 10], [200, 20], [300, 30], [400, 40], [600, 15]]
输出:9500

样例5:

输入:distance = 50, n = 1, gas_stations = [[25, 100]]
输出:5000

问题分析

这是一个动态规划问题,主要难点在于:

  • 需要考虑油箱容量限制(400L)

  • 起点和终点都需要保证200L油量

  • 需要在多个加油站之间做出最优选择

解题思路

1. 可行性检查

首先需要检查是否存在可行解:

if (validStations.get(0).get(0) > 200) {
    return -1;
}

// 检查相邻加油站之间的距离(油箱容量400L的限制)
for (int i = 0; i < n - 1; i++) {
    if (validStations.get(i + 1).get(0) - validStations.get(i).get(0) > 400) {
        return -1;
    }
}

// 检查最后一个加油站到终点的距离
if (distance - validStations.get(n - 1).get(0) > 400) {
    return -1;
}

2. 动态规划状态设计

long[][] dp = new long[n][401];
  • i: 表示当前到达的加油站编号

  • j: 表示当前剩余的油量

  • dp[i][j]: 表示达到该状态的最小成本

3. 状态转移

对于每个加油站,我们需要考虑:

  • 从上一个加油站到达当前加油站需要消耗的油量

  • 在当前加油站加多少油最优

4. 特殊情况处理

  • 距离小于等于200km的情况:
    return 0;  // 不需要加油就能到达
}

初始状态的处理:

int firstDistance = validStations.get(0).get(0);
int firstPrice = validStations.get(0).get(1);
for (int remain = 0; remain <= 400; remain++) {
    if (200 - firstDistance + remain <= 400 && remain >= 0) {
        dp[0][remain] = Math.max(0, remain - (200 - firstDistance)) * (long)firstPrice;
    }
}

代码如下

import java.util.Arrays;
import java.util.List;

public class Main {
    public static int solution(int distance, int n, List<List<Integer>> gas_stations) {
        if (n == 0) return -1;
    
        // 创建新的列表来存储有效的加油站,并进行深拷贝
        List<List<Integer>> validStations = new ArrayList<>();
        for (List<Integer> station : gas_stations) {
            if (station.size() >= 2) {
                validStations.add(new ArrayList<>(station));
            }
        }
        
        // 更新n为有效加油站的数量
        n = validStations.size();
        if (n == 0) return -1;
        
        // 如果目的地距离小于等于200,直接返回0
        if (distance <= 200) {
            return 0;
        }
        
        // 将加油站按距离排序,处理距离相同的情况
        validStations.sort((a, b) -> {
            int distCompare = a.get(0).compareTo(b.get(0));
            if (distCompare == 0) {
                return a.get(1).compareTo(b.get(1));
            }
            return distCompare;
        });
        
        // 检查第一个加油站是否太远
        if (validStations.get(0).get(0) > 200) {
            return -1;
        }
        
        // 检查相邻加油站之间的距离
        for (int i = 0; i < n - 1; i++) {
            if (validStations.get(i + 1).get(0) - validStations.get(i).get(0) > 400) {
                return -1;
            }
        }
        
        // 检查最后一个加油站到终点的距离
        if (distance - validStations.get(n - 1).get(0) > 400) {
            return -1;
        }
        
        // dp[i][j] 表示到达第i个加油站时,剩余j升油的最小成本
        long[][] dp = new long[n][401];
        for (int i = 0; i < n; i++) {
            Arrays.fill(dp[i], Long.MAX_VALUE);
        }
        
        // 初始化第一个加油站的状态
        int firstDistance = validStations.get(0).get(0);
        int firstPrice = validStations.get(0).get(1);
        for (int remain = 0; remain <= 400; remain++) {
            if (200 - firstDistance + remain <= 400 && remain >= 0) {
                dp[0][remain] = Math.max(0, remain - (200 - firstDistance)) * (long)firstPrice;
            }
        }
        
        // 动态规划过程
        for (int i = 1; i < n; i++) {
            int distance_diff = validStations.get(i).get(0) - validStations.get(i-1).get(0);
            for (int remain = 0; remain <= 400; remain++) {
                for (int prev_remain = distance_diff; prev_remain <= 400; prev_remain++) {
                    if (dp[i-1][prev_remain] != Long.MAX_VALUE) {
                        int need = remain - (prev_remain - distance_diff);
                        if (need >= 0 && need <= 400 - (prev_remain - distance_diff)) {
                            dp[i][remain] = Math.min(dp[i][remain], 
                                dp[i-1][prev_remain] + (long)need * validStations.get(i).get(1));
                        }
                    }
                }
            }
        }
        
        // 计算最终结果
        long result = Long.MAX_VALUE;
        int lastDistance = distance - validStations.get(n-1).get(0);
        for (int remain = 200 + lastDistance; remain <= 400; remain++) {
            if (dp[n-1][remain] != Long.MAX_VALUE) {
                result = Math.min(result, dp[n-1][remain]);
            }
        }
        
        return result == Long.MAX_VALUE ? -1 : (int)result;
    }

    public static void main(String[] args) {
        List<List<Integer>> gasStations1 = new ArrayList<>();
        gasStations1.add(List.of(100, 1));
        gasStations1.add(List.of(200, 30));
        gasStations1.add(List.of(400, 40));
        gasStations1.add(List.of(300, 20));

        List<List<Integer>> gasStations2 = new ArrayList<>();
        gasStations2.add(List.of(100, 999));
        gasStations2.add(List.of(150, 888));
        gasStations2.add(List.of(200, 777));
        gasStations2.add(List.of(300, 999));
        gasStations2.add(List.of(400, 1009));
        gasStations2.add(List.of(450, 1019));
        gasStations2.add(List.of(500, 1399));

        List<List<Integer>> gasStations3 = new ArrayList<>();
        gasStations3.add(List.of(101));
        gasStations3.add(List.of(100, 100));
        gasStations3.add(List.of(102, 1));

        List<List<Integer>> gasStations4 = new ArrayList<>();
        gasStations4.add(List.of(34, 1));
        gasStations4.add(List.of(105, 9));
        gasStations4.add(List.of(9, 10));
        gasStations4.add(List.of(134, 66));
        gasStations4.add(List.of(215, 90));
        gasStations4.add(List.of(999, 1999));
        gasStations4.add(List.of(49, 0));
        gasStations4.add(List.of(10, 1999));
        gasStations4.add(List.of(200, 2));
        gasStations4.add(List.of(300, 500));
        gasStations4.add(List.of(12, 34));
        gasStations4.add(List.of(1, 23));
        gasStations4.add(List.of(46, 20));
        gasStations4.add(List.of(80, 12));
        gasStations4.add(List.of(1, 1999));
        gasStations4.add(List.of(90, 33));
        gasStations4.add(List.of(101, 23));
        gasStations4.add(List.of(34, 88));
        gasStations4.add(List.of(103, 0));
        gasStations4.add(List.of(1, 1));

        System.out.println(solution(500, 4, gasStations1) == 4300);
        System.out.println(solution(500, 7, gasStations2) == 410700);
        System.out.println(solution(500, 3, gasStations3) == -1);
        System.out.println(solution(100, 20, gasStations4) == 0);
        System.out.println(solution(100, 0, new ArrayList<>()) == -1);
    }
}

复杂度分析

  • 时间复杂度:O(n C C),其中n是加油站数量,C是油箱容量(400)

  • 空间复杂度:O(n C)