|
|
package class40;
|
|
|
|
|
|
// 来自腾讯
|
|
|
// 比如arr = {3,1,2,4}
|
|
|
// 下标对应是:0 1 2 3
|
|
|
// 你最开始选择一个下标进行操作,一旦最开始确定了是哪个下标,以后都只能在这个下标上进行操作
|
|
|
// 比如你选定1下标,1下标上面的数字是1,你可以选择变化这个数字,比如你让这个数字变成2
|
|
|
// 那么arr = {3,2,2,4}
|
|
|
// 下标对应是:0 1 2 3
|
|
|
// 因为你最开始确定了1这个下标,所以你以后都只能对这个下标进行操作,
|
|
|
// 但是,和你此时下标上的数字一样的、且位置连成一片的数字,会跟着一起变
|
|
|
// 比如你选择让此时下标1的数字2变成3,
|
|
|
// 那么arr = {3,3,3,4} 可以看到下标1和下标2的数字一起变成3,这是规则!一定会一起变
|
|
|
// 下标对应是:0 1 2 3
|
|
|
// 接下来,你还是只能对1下标进行操作,那么数字一样的、且位置连成一片的数字(arr[0~2]这个范围)都会一起变
|
|
|
// 决定变成4
|
|
|
// 那么arr = {4,4,4,4}
|
|
|
// 下标对应是:0 1 2 3
|
|
|
// 至此,所有数都成一样的了,你在下标1上做了3个决定(第一次变成2,第二次变成3,第三次变成4),
|
|
|
// 因为联动规则,arr全刷成一种数字了
|
|
|
// 给定一个数组arr,最开始选择哪个下标,你随意
|
|
|
// 你的目的是一定要让arr都成为一种数字,注意联动效果会一直生效
|
|
|
// 返回最小的变化数
|
|
|
// arr长度 <= 200, arr中的值 <= 10^6
|
|
|
public class Code05_AllSame {
|
|
|
|
|
|
public static int allSame1(int[] arr) {
|
|
|
int ans = Integer.MAX_VALUE;
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
ans = Math.min(ans, process1(arr, i - 1, arr[i], i + 1));
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// 左边arr[0..left],如果left == -1,说明没有左边了
|
|
|
// 右边arr[right...n-1],如果right == n,说明没有右边了
|
|
|
// 中间的值是midV,中间的值代表中间一整个部分的值,中间部分有可能是一个数,也可能是一堆数,但是值都为midV
|
|
|
// 返回arr都刷成一样的,最小代价是多少
|
|
|
// left 可能性 : N
|
|
|
// right 可能性 : N
|
|
|
// midV 可能性 : arr中的最大值!
|
|
|
public static int process1(int[] arr, int left, int midV, int right) {
|
|
|
for (; left >= 0 && arr[left] == midV;) {
|
|
|
left--;
|
|
|
}
|
|
|
for (; right < arr.length && arr[right] == midV;) {
|
|
|
right++;
|
|
|
}
|
|
|
if (left == -1 && right == arr.length) {
|
|
|
return 0;
|
|
|
}
|
|
|
int p1 = Integer.MAX_VALUE;
|
|
|
if (left >= 0) {
|
|
|
p1 = process1(arr, left - 1, arr[left], right) + 1;
|
|
|
}
|
|
|
int p2 = Integer.MAX_VALUE;
|
|
|
if (right < arr.length) {
|
|
|
p2 = process1(arr, left, arr[right], right + 1) + 1;
|
|
|
}
|
|
|
return Math.min(p1, p2);
|
|
|
}
|
|
|
|
|
|
public static int allSame2(int[] arr) {
|
|
|
int ans = Integer.MAX_VALUE;
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
ans = Math.min(ans, process2(arr, i - 1, true, i + 1));
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
// 左边arr[0..l],如果left == -1,说明没有左边了
|
|
|
// 右边arr[r...n-1],如果right == n,说明没有右边了
|
|
|
// 中间的值代表arr[l+1...r-1]这个部分的值已经刷成了一种
|
|
|
// 中间的值,如果和arr[l+1]一样,isLeft就是true
|
|
|
// 中间的值,如果和arr[r-1]一样,isLeft就是false
|
|
|
// 返回arr都刷成一样的,最小代价是多少
|
|
|
// left 可能性 : N
|
|
|
// right 可能性 : N
|
|
|
// isLeft 可能性 : true/false,两种
|
|
|
public static int process2(int[] arr, int l, boolean isLeft, int r) {
|
|
|
int left = l;
|
|
|
for (; left >= 0 && arr[left] == (isLeft ? arr[l + 1] : arr[r - 1]);) {
|
|
|
left--;
|
|
|
}
|
|
|
int right = r;
|
|
|
for (; right < arr.length && arr[right] == (isLeft ? arr[l + 1] : arr[r - 1]);) {
|
|
|
right++;
|
|
|
}
|
|
|
if (left == -1 && right == arr.length) {
|
|
|
return 0;
|
|
|
}
|
|
|
int p1 = Integer.MAX_VALUE;
|
|
|
if (left >= 0) {
|
|
|
p1 = process2(arr, left - 1, true, right) + 1;
|
|
|
}
|
|
|
int p2 = Integer.MAX_VALUE;
|
|
|
if (right < arr.length) {
|
|
|
p2 = process2(arr, left, false, right + 1) + 1;
|
|
|
}
|
|
|
return Math.min(p1, p2);
|
|
|
}
|
|
|
// 如上的递归,请改动态规划,具体参考体系学习班,动态规划大章节!
|
|
|
|
|
|
// 为了测试
|
|
|
public static int[] randomArray(int maxSize, int maxNum) {
|
|
|
int size = 2 + (int) (Math.random() * maxSize);
|
|
|
int[] arr = new int[size];
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
arr[i] = 1 + (int) (Math.random() * maxSize);
|
|
|
}
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
// 为了测试
|
|
|
public static void main(String[] args) {
|
|
|
System.out.println("测试开始");
|
|
|
for (int i = 0; i < 10000; i++) {
|
|
|
int[] arr = randomArray(20, 10);
|
|
|
int ans1 = allSame1(arr);
|
|
|
int ans2 = allSame2(arr);
|
|
|
if (ans1 != ans2) {
|
|
|
System.out.println("出错了!!!");
|
|
|
for (int i1 : arr) {
|
|
|
System.out.print(i1 + " ");
|
|
|
}
|
|
|
System.out.println();
|
|
|
System.out.println(ans1);
|
|
|
System.out.println(ans2);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
System.out.println("测试结束");
|
|
|
}
|
|
|
|
|
|
}
|