思路1
DFS with memo
Solution:
Time: O(m * n^2)
Space: O(n^2)
public class Solution {
public int books(ArrayList<Integer> A, int B) {
if (A.size() == 0 || B > A.size()) return -1;
List<Integer> books = new ArrayList<>(A);
int[][] memo = new int[A.size() + 1][B + 1];
return partition(books, B, memo);
}
private int sum(List<Integer> list, int from, int to) {
int sum = 0;
for (int i = from; i <= to; i ++) {
sum += list.get(i);
}
return sum;
}
private int partition(List<Integer> A, int B, int[][] memo) {
int n = A.size();
if (memo[n][B] != 0) {
return memo[n][B];
}
if (B == 1) {
return sum(A, 0, n - 1);
}
if (n == 1) {
return A.get(0);
}
int minPage = Integer.MAX_VALUE;
for (int i = 0; i <= n; i ++) {
minPage = Math.min(minPage, Math.max(partition(A.subList(0, i), B - 1, memo), sum(A, i, n - 1)));
}
memo[n][B] = minPage;
return minPage;
}
}
dp:
Time: O(m * n^2)
Space: O(n^2)
public class Solution {
public int books(ArrayList<Integer> A, int B) {
if (A.size() == 0 || B > A.size()) return -1;
List<Integer> books = new ArrayList<>(A);
return partition(books, B);
}
private int sum(List<Integer> list, int from, int to) {
int sum = 0;
for (int i = from; i <= to; i ++) {
sum += list.get(i);
}
return sum;
}
private int partition(List<Integer> A, int B) {
int n = A.size();
int[][] dp = new int[n + 1][B + 1];
// n = 1
for (int i = 1; i <= B; i ++) {
dp[1][i] = A.get(0);
}
// B = 1
for (int i = 1; i <= n; i ++) {
dp[i][1] = sum(A, 0, i - 1);
}
for (int i = 2; i <= B; i ++) {
for (int j = 2; j <= n; j ++) {
int minPage = Integer.MAX_VALUE;
for (int p = 1; p <= j; p ++) {
minPage = Math.min(minPage, Math.max(dp[p][i - 1], sum(A, p, j - 1)));
}
dp[j][i] = minPage;
}
}
return dp[n][B];
}
}
思路2
We can actually use binary search for this problem.
The minimum possible number is the max value in the array, in the case when M = size of the array, so every student has one book.
The maximum possible number is the the sum of the array, in the case when M = 1, so the one student read all the books.
If we are given a number of max pages a student reads, we can use it to calculate the number of students needed to finish reading all the books.
if the number of students is > M, then we know our max page is too small, otherwise it's too big.
Again, because left, and right will keep changing until we find the minimum maxBooks, it's guaranteed that the answer is one of the sum of elements in the array.
Solution:
Time: O(nlog(sum(A)))
Space: O(1)
public class Solution {
public int books(ArrayList<Integer> A, int B) {
if (A.size() == 0 || B > A.size()) return -1;
List<Integer> books = new ArrayList<>(A);
return partition(books, B);
}
private int max(List<Integer> list) {
int m = list.get(0);
for (int i = 1; i < list.size(); i ++) {
m = Math.max(m, list.get(i));
}
return m;
}
private int sum(List<Integer> list, int from, int to) {
int sum = 0;
for (int i = from; i <= to; i ++) {
sum += list.get(i);
}
return sum;
}
private int numberOfStudents(List<Integer> list, int maxBooks) {
int num = 1;
int currBook = 0;
for (int i = 0; i < list.size(); i ++) {
currBook += list.get(i);
if (currBook > maxBooks) {
num ++;
currBook = list.get(i);
}
}
return num;
}
private int partition(List<Integer> A, int B) {
int n = A.size();
int left = max(A);
int right = sum(A, 0, n - 1);
while (left <= right) {
int mid = left + (right - left) / 2;
if (numberOfStudents(A, mid) <= B) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
}