public class Code_SplitArrayLargestSum {
// 求原数组arr[L...R]的累加和
public static int sum(int[] sum, int L, int R) {
return sum[R + 1] - sum[L];
}
// 不优化枚举的动态规划方法,O(N^2 * K)
public static int splitArray1(int[] nums, int K) {
int N = nums.length;
int[] sum = new int[N + 1];
for (int i = 0; i < N; i++) {
sum[i + 1] = sum[i] + nums[i];
}
int[][] dp = new int[N][K + 1];
for (int j = 1; j <= K; j++) {
dp[0][j] = nums[0];
}
for (int i = 1; i < N; i++) {
dp[i][1] = sum(sum, 0, i);
}
// 每一行从上往下
// 每一列从左往右
// 根本不去凑优化位置对儿!
for (int i = 1; i < N; i++) {
for (int j = 2; j <= K; j++) {
int ans = Integer.MAX_VALUE;
// 枚举是完全不优化的!
for (int leftEnd = 0; leftEnd <= i; leftEnd++) {
int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1];
int rightCost = leftEnd == i ? 0 : sum(sum, leftEnd + 1, i);
int cur = Math.max(leftCost, rightCost);
if (cur < ans) {
ans = cur;
}
}
dp[i][j] = ans;
}
}
return dp[N - 1][K];
}
// 课上现场写的方法,用了枚举优化,O(N * K)
public static int splitArray2(int[] nums, int K) {
int N = nums.length;
int[] sum = new int[N + 1];
for (int i = 0; i < N; i++) {
sum[i + 1] = sum[i] + nums[i];
}
int[][] dp = new int[N][K + 1];
int[][] best = new int[N][K + 1];
// 第0行
for (int j = 1; j <= K; j++) {
dp[0][j] = nums[0];
best[0][j] = -1;
}
// 第1列
for (int i = 1; i < N; i++) {
dp[i][1] = sum(sum,0,i);
best[i][1] = -1;
}
for (int j = 2; j <= K; j++) {
for (int i = N-1; i >=1; i--) {
int up = (i == N-1) ? N-1 : best[i+1][j];
int down = best[i][j-1];
int ans = Integer.MAX_VALUE;
int bestChose = -1;
for (int leftEnd = down; leftEnd <= up; leftEnd++) {
int leftCost = (leftEnd == -1) ? 0 : dp[leftEnd][j-1];
int rightCost = (leftEnd == i) ? 0 : sum(sum,leftEnd+1,i);
int cur = Math.max(leftCost,rightCost);
if(cur < ans){
ans = cur;
bestChose = leftEnd;
}
}
dp[i][j] = ans;
best[i][j] = bestChose;
}
}
return dp[N-1][K];
}
public static int splitArray3(int[] nums, int M) {
long sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
long l = 0;
long r = sum;
long ans = 0;
while (l <= r) {
long mid = (l + r) / 2;
long cur = getNeedParts(nums, mid);
if (cur <= M) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return (int) ans;
}
public static int getNeedParts(int[] arr, long aim) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] > aim) {
return Integer.MAX_VALUE;
}
}
int parts = 1;
int all = arr[0];
for (int i = 1; i < arr.length; i++) {
if (all + arr[i] > aim) {
parts++;
all = arr[i];
} else {
all += arr[i];
}
}
return parts;
}
public static int[] randomArray(int len, int maxValue) {
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int) (Math.random() * maxValue);
}
return arr;
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int N = 100;
int maxValue = 100;
int testTime = 10000;
System.out.println("测试开始 Code_SplitArrayLargestSum");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * N) + 1;
int M = (int) (Math.random() * N) + 1;
int[] arr = randomArray(len, maxValue);
int ans1 = splitArray1(arr, M);
int ans2 = splitArray2(arr, M);
int ans3 = splitArray3(arr, M);
if (ans1 != ans2 || ans1 != ans3) {
System.out.print("arr : ");
printArray(arr);
System.out.println("M : " + M);
System.out.println("ans1 : " + ans1);
System.out.println("ans2 : " + ans2);
System.out.println("ans3 : " + ans3);
System.out.println("Oops!");
break;
}
}
System.out.println("测试结束 Code_SplitArrayLargestSum");
}
}