|
|
package class43;
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
// 来自360笔试
|
|
|
// 给定一个正数数组arr,长度为n,下标0~n-1
|
|
|
// arr中的0、n-1位置不需要达标,它们分别是最左、最右的位置
|
|
|
// 中间位置i需要达标,达标的条件是 : arr[i-1] > arr[i] 或者 arr[i+1] > arr[i]哪个都可以
|
|
|
// 你每一步可以进行如下操作:对任何位置的数让其-1
|
|
|
// 你的目的是让arr[1~n-2]都达标,这时arr称之为yeah!数组
|
|
|
// 返回至少要多少步可以让arr变成yeah!数组
|
|
|
// 数据规模 : 数组长度 <= 10000,数组中的值<=500
|
|
|
public class Code02_MinCostToYeahArray {
|
|
|
|
|
|
public static final int INVALID = Integer.MAX_VALUE;
|
|
|
|
|
|
// 纯暴力方法,只是为了结果对
|
|
|
// 时间复杂度极差
|
|
|
public static int minCost0(int[] arr) {
|
|
|
if (arr == null || arr.length < 3) {
|
|
|
return 0;
|
|
|
}
|
|
|
int n = arr.length;
|
|
|
int min = INVALID;
|
|
|
for (int num : arr) {
|
|
|
min = Math.min(min, num);
|
|
|
}
|
|
|
int base = min - n;
|
|
|
return process0(arr, base, 0);
|
|
|
}
|
|
|
|
|
|
public static int process0(int[] arr, int base, int index) {
|
|
|
if (index == arr.length) {
|
|
|
for (int i = 1; i < arr.length - 1; i++) {
|
|
|
if (arr[i - 1] <= arr[i] && arr[i] >= arr[i + 1]) {
|
|
|
return INVALID;
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
} else {
|
|
|
int ans = INVALID;
|
|
|
int tmp = arr[index];
|
|
|
for (int cost = 0; arr[index] >= base; cost++, arr[index]--) {
|
|
|
int next = process0(arr, base, index + 1);
|
|
|
if (next != INVALID) {
|
|
|
ans = Math.min(ans, cost + next);
|
|
|
}
|
|
|
}
|
|
|
arr[index] = tmp;
|
|
|
return ans;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 递归方法,已经把尝试写出
|
|
|
public static int minCost1(int[] arr) {
|
|
|
if (arr == null || arr.length < 3) {
|
|
|
return 0;
|
|
|
}
|
|
|
int min = INVALID;
|
|
|
for (int num : arr) {
|
|
|
min = Math.min(min, num);
|
|
|
}
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
arr[i] += arr.length - min;
|
|
|
}
|
|
|
return process1(arr, 1, arr[0], true);
|
|
|
}
|
|
|
|
|
|
// 当前来到index位置,值arr[index]
|
|
|
// pre : 前一个位置的值,可能减掉了一些,所以不能用arr[index-1]
|
|
|
// preOk : 前一个位置的值,是否被它左边的数变有效了
|
|
|
// 返回 : 让arr都变有效,最小代价是什么?
|
|
|
public static int process1(int[] arr, int index, int pre, boolean preOk) {
|
|
|
if (index == arr.length - 1) { // 已经来到最后一个数了
|
|
|
return preOk || pre < arr[index] ? 0 : INVALID;
|
|
|
}
|
|
|
// 当前index,不是最后一个数!
|
|
|
int ans = INVALID;
|
|
|
if (preOk) {
|
|
|
for (int cur = arr[index]; cur >= 0; cur--) {
|
|
|
int next = process1(arr, index + 1, cur, cur < pre);
|
|
|
if (next != INVALID) {
|
|
|
ans = Math.min(ans, arr[index] - cur + next);
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
for (int cur = arr[index]; cur > pre; cur--) {
|
|
|
int next = process1(arr, index + 1, cur, false);
|
|
|
if (next != INVALID) {
|
|
|
ans = Math.min(ans, arr[index] - cur + next);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// 初改动态规划方法,就是参考minCost1,改出来的版本
|
|
|
public static int minCost2(int[] arr) {
|
|
|
if (arr == null || arr.length < 3) {
|
|
|
return 0;
|
|
|
}
|
|
|
int min = INVALID;
|
|
|
for (int num : arr) {
|
|
|
min = Math.min(min, num);
|
|
|
}
|
|
|
int n = arr.length;
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
arr[i] += n - min;
|
|
|
}
|
|
|
int[][][] dp = new int[n][2][];
|
|
|
for (int i = 1; i < n; i++) {
|
|
|
dp[i][0] = new int[arr[i - 1] + 1];
|
|
|
dp[i][1] = new int[arr[i - 1] + 1];
|
|
|
Arrays.fill(dp[i][0], INVALID);
|
|
|
Arrays.fill(dp[i][1], INVALID);
|
|
|
}
|
|
|
for (int pre = 0; pre <= arr[n - 2]; pre++) {
|
|
|
dp[n - 1][0][pre] = pre < arr[n - 1] ? 0 : INVALID;
|
|
|
dp[n - 1][1][pre] = 0;
|
|
|
}
|
|
|
for (int index = n - 2; index >= 1; index--) {
|
|
|
for (int pre = 0; pre <= arr[index - 1]; pre++) {
|
|
|
for (int cur = arr[index]; cur > pre; cur--) {
|
|
|
int next = dp[index + 1][0][cur];
|
|
|
if (next != INVALID) {
|
|
|
dp[index][0][pre] = Math.min(dp[index][0][pre], arr[index] - cur + next);
|
|
|
}
|
|
|
}
|
|
|
for (int cur = arr[index]; cur >= 0; cur--) {
|
|
|
int next = dp[index + 1][cur < pre ? 1 : 0][cur];
|
|
|
if (next != INVALID) {
|
|
|
dp[index][1][pre] = Math.min(dp[index][1][pre], arr[index] - cur + next);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return dp[1][1][arr[0]];
|
|
|
}
|
|
|
|
|
|
// minCost2动态规划 + 枚举优化
|
|
|
// 改出的这个版本,需要一些技巧,但很可惜不是最优解
|
|
|
// 虽然不是最优解,也足以通过100%的case了,
|
|
|
// 这种技法的练习,非常有意义
|
|
|
public static int minCost3(int[] arr) {
|
|
|
if (arr == null || arr.length < 3) {
|
|
|
return 0;
|
|
|
}
|
|
|
int min = INVALID;
|
|
|
for (int num : arr) {
|
|
|
min = Math.min(min, num);
|
|
|
}
|
|
|
int n = arr.length;
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
arr[i] += n - min;
|
|
|
}
|
|
|
int[][][] dp = new int[n][2][];
|
|
|
for (int i = 1; i < n; i++) {
|
|
|
dp[i][0] = new int[arr[i - 1] + 1];
|
|
|
dp[i][1] = new int[arr[i - 1] + 1];
|
|
|
}
|
|
|
for (int p = 0; p <= arr[n - 2]; p++) {
|
|
|
dp[n - 1][0][p] = p < arr[n - 1] ? 0 : INVALID;
|
|
|
}
|
|
|
int[][] best = best(dp, n - 1, arr[n - 2]);
|
|
|
for (int i = n - 2; i >= 1; i--) {
|
|
|
for (int p = 0; p <= arr[i - 1]; p++) {
|
|
|
if (arr[i] < p) {
|
|
|
dp[i][1][p] = best[1][arr[i]];
|
|
|
} else {
|
|
|
dp[i][1][p] = Math.min(best[0][p], p > 0 ? best[1][p - 1] : INVALID);
|
|
|
}
|
|
|
dp[i][0][p] = arr[i] <= p ? INVALID : best[0][p + 1];
|
|
|
}
|
|
|
best = best(dp, i, arr[i - 1]);
|
|
|
}
|
|
|
return dp[1][1][arr[0]];
|
|
|
}
|
|
|
|
|
|
public static int[][] best(int[][][] dp, int i, int v) {
|
|
|
int[][] best = new int[2][v + 1];
|
|
|
best[0][v] = dp[i][0][v];
|
|
|
for (int p = v - 1; p >= 0; p--) {
|
|
|
best[0][p] = dp[i][0][p] == INVALID ? INVALID : v - p + dp[i][0][p];
|
|
|
best[0][p] = Math.min(best[0][p], best[0][p + 1]);
|
|
|
}
|
|
|
best[1][0] = dp[i][1][0] == INVALID ? INVALID : v + dp[i][1][0];
|
|
|
for (int p = 1; p <= v; p++) {
|
|
|
best[1][p] = dp[i][1][p] == INVALID ? INVALID : v - p + dp[i][1][p];
|
|
|
best[1][p] = Math.min(best[1][p], best[1][p - 1]);
|
|
|
}
|
|
|
return best;
|
|
|
}
|
|
|
|
|
|
// 最终的最优解,贪心
|
|
|
// 时间复杂度O(N)
|
|
|
// 请注意,重点看上面的方法
|
|
|
// 这个最优解容易理解,但让你学到的东西不是很多
|
|
|
public static int yeah(int[] arr) {
|
|
|
if (arr == null || arr.length < 3) {
|
|
|
return 0;
|
|
|
}
|
|
|
int n = arr.length;
|
|
|
int[] nums = new int[n + 2];
|
|
|
nums[0] = Integer.MAX_VALUE;
|
|
|
nums[n + 1] = Integer.MAX_VALUE;
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
nums[i + 1] = arr[i];
|
|
|
}
|
|
|
int[] leftCost = new int[n + 2];
|
|
|
int pre = nums[0];
|
|
|
int change = 0;
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
change = Math.min(pre - 1, nums[i]);
|
|
|
leftCost[i] = nums[i] - change + leftCost[i - 1];
|
|
|
pre = change;
|
|
|
}
|
|
|
int[] rightCost = new int[n + 2];
|
|
|
pre = nums[n + 1];
|
|
|
for (int i = n; i >= 1; i--) {
|
|
|
change = Math.min(pre - 1, nums[i]);
|
|
|
rightCost[i] = nums[i] - change + rightCost[i + 1];
|
|
|
pre = change;
|
|
|
}
|
|
|
int ans = Integer.MAX_VALUE;
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
ans = Math.min(ans, leftCost[i] + rightCost[i + 1]);
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static int[] randomArray(int len, int v) {
|
|
|
int[] arr = new int[len];
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
arr[i] = (int) (Math.random() * v) + 1;
|
|
|
}
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static int[] copyArray(int[] arr) {
|
|
|
int[] ans = new int[arr.length];
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
ans[i] = arr[i];
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static void main(String[] args) {
|
|
|
int len = 7;
|
|
|
int v = 10;
|
|
|
int testTime = 100;
|
|
|
System.out.println("==========");
|
|
|
System.out.println("功能测试开始");
|
|
|
for (int i = 0; i < testTime; i++) {
|
|
|
int n = (int) (Math.random() * len) + 1;
|
|
|
int[] arr = randomArray(n, v);
|
|
|
int[] arr0 = copyArray(arr);
|
|
|
int[] arr1 = copyArray(arr);
|
|
|
int[] arr2 = copyArray(arr);
|
|
|
int[] arr3 = copyArray(arr);
|
|
|
int[] arr4 = copyArray(arr);
|
|
|
int ans0 = minCost0(arr0);
|
|
|
int ans1 = minCost1(arr1);
|
|
|
int ans2 = minCost2(arr2);
|
|
|
int ans3 = minCost3(arr3);
|
|
|
int ans4 = yeah(arr4);
|
|
|
if (ans0 != ans1 || ans0 != ans2 || ans0 != ans3 || ans0 != ans4) {
|
|
|
System.out.println("出错了!");
|
|
|
}
|
|
|
}
|
|
|
System.out.println("功能测试结束");
|
|
|
System.out.println("==========");
|
|
|
|
|
|
System.out.println("性能测试开始");
|
|
|
|
|
|
len = 10000;
|
|
|
v = 500;
|
|
|
System.out.println("生成随机数组长度:" + len);
|
|
|
System.out.println("生成随机数组值的范围:[1, " + v + "]");
|
|
|
int[] arr = randomArray(len, v);
|
|
|
int[] arr3 = copyArray(arr);
|
|
|
int[] arrYeah = copyArray(arr);
|
|
|
long start;
|
|
|
long end;
|
|
|
start = System.currentTimeMillis();
|
|
|
int ans3 = minCost3(arr3);
|
|
|
end = System.currentTimeMillis();
|
|
|
System.out.println("minCost3方法:");
|
|
|
System.out.println("运行结果: " + ans3 + ", 时间(毫秒) : " + (end - start));
|
|
|
|
|
|
start = System.currentTimeMillis();
|
|
|
int ansYeah = yeah(arrYeah);
|
|
|
end = System.currentTimeMillis();
|
|
|
System.out.println("yeah方法:");
|
|
|
System.out.println("运行结果: " + ansYeah + ", 时间(毫秒) : " + (end - start));
|
|
|
|
|
|
System.out.println("性能测试结束");
|
|
|
System.out.println("==========");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|