环形数组中的最大贡献
问题描述
小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)。
算法步骤
-
初始化:
- 定义一个变量
max来存储当前找到的最大贡献值,初始值为Integer.MIN_VALUE。 - 定义一个变量
dis来存储当前下标对(i, j)的距离。
- 定义一个变量
-
遍历所有下标对:
-
使用两个嵌套的
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。
-
-
返回结果:
- 返回
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)