有问题欢迎指导
问题描述
某石油公司需要向A、B两地运输石油。两地的需求量不同,而一辆车只能装载一定量的石油。经过计算A地需要a辆车运输、B地需要b辆车运输,才能满足需求。现在一共有n辆车分布在各地,每辆车前往A、B两地运输石油均可以获得一定不等的利润。现在请你安排a辆车前往A地,b辆车前往B地运输石油,使得在满足A、B两地石油需求的前提下,获得最大的利润。每辆车只能前往一地运输石油。
问题分析
简化分析
首先我们分析只有一辆车情况。求最大利润。
那么我们可以选择贪心、动态规划、枚举法来进行。一辆车应该贪心是最好写的了。
枚举法肯定不考虑,通不过。
贪心
分析
对于贪心,每次选择最大利润。解决这个问题很好解决。
那么我们尝试应用到这个题目上。利润排序后选取最大利润,但是这里有个问题,一辆车选择后,另一辆车的利润数据需要删除掉。
排序:我选取的是优先队列进行的。
代码
参数解释(未加注解的):
list
第一个数表示利润
第二个数表示第几个车
第三个数表示去往哪个地方1表示a 0表示b
set
已经派出的车
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();//车的数量
int a = sc.nextInt();//a地需要的
int b = sc.nextInt();//b地需要的
Queue<List<Integer>> q = new PriorityQueue<>((Comparator.comparingInt(o -> -o.get(0))));
for (int i = 0; i < 2*n; i++) {
int award = sc.nextInt();
List<Integer> list = new ArrayList<>();
list.add(award);
list.add(i/2);
list.add(i%2==0?1:0);
q.add(list);
}
int count = 0;
Set<Integer> set = new HashSet<>();
while (!q.isEmpty()&&a>0||b>0){
List<Integer> poll = q.poll();
if (set.add(poll.get(1))) {
Integer i = poll.get(2);
if (i == 1) {
if (a==0)
continue;
else
a--;
}
else {
if (b==0)
continue;
else
b--;
}
count += poll.get(0);
}
}
System.out.println(count);
下面是chatgpt给的代码分析
该代码主要实现了一个奖励队列,根据奖励的多少和是否已经被选择过来进行排序,同时记录每个奖励对应的车辆和目的地(a或b)。
在主循环中,每次从队列中取出奖励最多且未被选择过的项,
然后根据其对应的目的地来判断该奖励是否可以用于该目的地的车辆,并将奖励计入总奖励中。
该算法的时间复杂度为 O(n log n),其中 n 是车辆的数量。
这是由于在主循环中,每次需要从优先队列中取出最大值,这需要花费 O(log n) 的时间。
因此,对于 n 个车辆,需要进行 n 次循环,因此总时间复杂度为 O(n log n)。
动态规划
分析
对于一个地方,我们用动态规划怎么做?
用
d
p
[
x
]
[
y
]
dp[x][y]
dp[x][y]表示1-x辆车去这个地方y辆车的最大利润
或者我们用
d
p
[
y
]
dp[y]
dp[y]表示去这个地方y辆车的最大利润,通过遍历车数来
我们不能j从1开始往上:
试想如果这样那么将会出现dp[2]选择了这个车并且变大了,而dp[3]又选则这个车dp[3]=dp[2]+车的价值。则会重复选择。
上界:
如果
a
>
i
a>i
a>i ,我们最多只能派 i辆,而不是 a 辆车
如果
a
≤
i
a\leq i
a≤i,我们最多也只需要派 a 辆车
下界:
即求在
i
i
i辆车最少选择的
j
j
j的数量。
因为我们去这个地方的和一定是a
那么我们在
i
i
i派出了
j
j
j辆车,则还需要
a
−
j
a-j
a−j辆车去这个地方
则剩下车
n
−
i
n-i
n−i的车需要填满这个空缺
我们可以得到
n
−
i
+
j
>
=
a
n-i+j>=a
n−i+j>=a则
j
>
=
a
−
n
+
i
j>=a-n+i
j>=a−n+i但是这个数不能比0小。
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int a = sc.nextInt();
int[] dp = new int[a+1];
for (int i = 1; i <= n; i++) {//第几辆车
int award = sc.nextInt();
for (int j = Math.min(a,i); j > Math.max(0,a-n+i-1); j--) {//遍历去a地的车的数量为j
dp[j] = Math.max(dp[j], dp[j - 1] + award);
}
}
System.out.println(dp[a]);
接下来我们来解决2地的问题。一样的我们可以选择
d
p
[
x
]
[
y
]
[
z
]
dp[x][y][z]
dp[x][y][z] x代表1-x辆车的最优解,y代表去a地的车,z代表去b地的车。
也可以
d
p
[
x
]
[
y
]
dp[x][y]
dp[x][y]表示去a地x辆车,去b地y辆车的最大利润。
从每个车辆的利润开始迭代,更新 dp 数组。
不从1开始在一辆车和上界、下界我们分析过了。
我们来看B地
因为我们去这个地方的和一定是b
那么我们在
i
−
x
i-x
i−x派出了
y
y
y辆车,则还需要
b
−
y
b-y
b−y辆车去b地方,需要
a
−
x
a-x
a−x去a地
则剩下车
n
−
i
n-i
n−i的车需要填满这个空缺
我们可以得到
n
−
i
>
=
b
−
y
+
a
−
x
n-i>=b-y+a-x
n−i>=b−y+a−x则
y
>
=
b
+
a
−
x
−
n
+
i
y>=b+a-x-n+i
y>=b+a−x−n+i但是这个数不能比0小。
代码
参数介绍
x 和 y 分别是派往 A 和 B 地的车辆数
i 是当前考虑的车辆数
awardA 和 awardB 分别是派往 A 和 B 地的车辆所能获得的利润
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();//n辆车
int a = sc.nextInt();//去a地需要的车
int b = sc.nextInt();//去b地需要的车
int[][] dp = new int[a + 1][b + 1];//dp[i][j]表示前i个a,选了j个b的最大价值
for (int i = 1; i <= n; i++) {//遍历所有的车
int awardA = sc.nextInt();
int awardB = sc.nextInt();
for (int x = Math.min(a, i); x >= Math.max(0,a-n+i); x--) {//先装满a
for (int y = Math.min(b, i - x); y >= Math.max(0,b+a-x-n+i); y--) {
if (x + y > i) break;
else if(x== 0 && y == 0) {
}
else if (x==0)
dp[x][y] = Math.max(dp[x][y], dp[x][y - 1] + awardB);
else if (y==0)
dp[x][y] = Math.max(dp[x][y], dp[x - 1][y] + awardA);
else
dp[x][y] = Math.max(dp[x][y],Math.max(dp[x - 1][y] + awardA, dp[x][y - 1] + awardB));
}
}
}
System.out.println(dp[a][b]);
下面是chatgpt给的代码分析,虽然感觉2分析有点怪
这段代码是一个动态规划算法,用于解决车辆选择问题。
它通过构建一个二维数组 dp 来保存前 i 辆车中选择 j 辆车去 a 地和剩下的车去 b 地所能获得的最大价值。
在主循环中,对于每一辆车,从 a 地开始尽可能地装满
同时更新 dp 数组,以便在下一次循环时使用。
.因此,该算法的时间复杂度为 O(n * a * b)
其中 n 是车辆数量,a 是需要去 a 地的车辆数量,b 是需要去 b 地的车辆数量。
对于每辆车,它需要遍历 dp 数组中的每个元素(即从 a 到 b 的所有可能的车辆组合)
因此需要遍历 O(a * b) 个元素。由于有 n 辆车,所以总的时间复杂度为 O(n * a * b)。