-
Notifications
You must be signed in to change notification settings - Fork 299
/
Code04_Drive.java
136 lines (126 loc) · 3.87 KB
/
Code04_Drive.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package class02;
import java.util.Arrays;
public class Code04_Drive {
// 课上的现场版本
// income -> N * 2 的矩阵 N是偶数!
// 0 [9, 13]
// 1 [45,60]
public static int maxMoney1(int[][] income) {
if (income == null || income.length < 2 || (income.length & 1) != 0) {
return 0;
}
int N = income.length; // 司机数量一定是偶数,所以才能平分,A N /2 B N/2
int M = N >> 1; // M = N / 2 要去A区域的人
return process1(income, 0, M);
}
// index.....所有的司机,往A和B区域分配!
// A区域还有rest个名额!
// 返回把index...司机,分配完,并且最终A和B区域同样多的情况下,index...这些司机,整体收入最大是多少!
public static int process1(int[][] income, int index, int rest) {
if (index == income.length) {
return 0;
}
// 还剩下司机!
if (income.length - index == rest) {
return income[index][0] + process1(income, index + 1, rest - 1);
}
if (rest == 0) {
return income[index][1] + process1(income, index + 1, rest);
}
// 当前司机,可以去A,或者去B
int p1 = income[index][0] + process1(income, index + 1, rest - 1);
int p2 = income[index][1] + process1(income, index + 1, rest);
return Math.max(p1, p2);
}
// 严格位置依赖的动态规划版本
public static int maxMoney2(int[][] income) {
int N = income.length;
int M = N >> 1;
int[][] dp = new int[N + 1][M + 1];
for (int i = N - 1; i >= 0; i--) {
for (int j = 0; j <= M; j++) {
if (N - i == j) {
dp[i][j] = income[i][0] + dp[i + 1][j - 1];
} else if (j == 0) {
dp[i][j] = income[i][1] + dp[i + 1][j];
} else {
int p1 = income[i][0] + dp[i + 1][j - 1];
int p2 = income[i][1] + dp[i + 1][j];
dp[i][j] = Math.max(p1, p2);
}
}
}
return dp[0][M];
}
// 这题有贪心策略 :
// 假设一共有10个司机,思路是先让所有司机去A,得到一个总收益
// 然后看看哪5个司机改换门庭(去B),可以获得最大的额外收益
// 这道题有贪心策略,打了我的脸
// 但是我课上提到的技巧请大家重视
// 根据数据量猜解法可以省去大量的多余分析,节省时间
// 这里感谢卢圣文同学
public static int maxMoney3(int[][] income) {
int N = income.length;
int[] arr = new int[N];
int sum = 0;
for (int i = 0; i < N; i++) {
arr[i] = income[i][1] - income[i][0];
sum += income[i][0];
}
Arrays.sort(arr);
int M = N >> 1;
for (int i = N - 1; i >= M; i--) {
sum += arr[i];
}
return sum;
}
// 找到了leetcode上的测试
// leetcode上让求最小,课上讲的求最大
// 其实是一个意思
// 测试链接 : https://leetcode.cn/problems/two-city-scheduling/
public static int twoCitySchedCost(int[][] costs) {
int N = costs.length;
int[] arr = new int[N];
int sum = 0;
for (int i = 0; i < N; i++) {
arr[i] = costs[i][1] - costs[i][0];
sum += costs[i][0];
}
Arrays.sort(arr);
int M = N >> 1;
for (int i = 0; i < M; i++) {
sum += arr[i];
}
return sum;
}
// 返回随机len*2大小的正数矩阵
// 值在0~value-1之间
public static int[][] randomMatrix(int len, int value) {
int[][] ans = new int[len << 1][2];
for (int i = 0; i < ans.length; i++) {
ans[i][0] = (int) (Math.random() * value);
ans[i][1] = (int) (Math.random() * value);
}
return ans;
}
public static void main(String[] args) {
int N = 10;
int value = 100;
int testTime = 500;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * N) + 1;
int[][] matrix = randomMatrix(len, value);
int ans1 = maxMoney1(matrix);
int ans2 = maxMoney2(matrix);
int ans3 = maxMoney3(matrix);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println(ans1);
System.out.println(ans2);
System.out.println(ans3);
System.out.println("Oops!");
}
}
System.out.println("测试结束");
}
}