|
|
|
|
package class09;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 给定一个数组arr,长度为N,arr中的值不是0就是1
|
|
|
|
|
* arr[i]表示第i栈灯的状态,0代表灭灯,1代表亮灯
|
|
|
|
|
* 每一栈灯都有开关,但是按下i号灯的开关,会同时改变i-1、i、i+2栈灯的状态
|
|
|
|
|
* 问题一:
|
|
|
|
|
* 如果N栈灯排成一条直线,请问最少按下多少次开关,能让灯都亮起来
|
|
|
|
|
* 排成一条直线说明:
|
|
|
|
|
* i为中间位置时,i号灯的开关能影响i-1、i和i+1
|
|
|
|
|
* 0号灯的开关只能影响0和1位置的灯
|
|
|
|
|
* N-1号灯的开关只能影响N-2和N-1位置的灯
|
|
|
|
|
*
|
|
|
|
|
* 问题二:
|
|
|
|
|
* 如果N栈灯排成一个圈,请问最少按下多少次开关,能让灯都亮起来
|
|
|
|
|
* 排成一个圈说明:
|
|
|
|
|
* i为中间位置时,i号灯的开关能影响i-1、i和i+1
|
|
|
|
|
* 0号灯的开关能影响N-1、0和1位置的灯
|
|
|
|
|
* N-1号灯的开关能影响N-2、N-1和0位置的灯
|
|
|
|
|
*
|
|
|
|
|
* */
|
|
|
|
|
public class Code01_LightProblem {
|
|
|
|
|
|
|
|
|
|
// 无环改灯问题的暴力版本
|
|
|
|
|
public static int noLoopRight(int[] arr) {
|
|
|
|
|
if (arr == null || arr.length == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 1) {
|
|
|
|
|
return arr[0] == 1 ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 2) {
|
|
|
|
|
return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
return f1(arr, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int f1(int[] arr, int i) {
|
|
|
|
|
if (i == arr.length) {
|
|
|
|
|
return valid(arr) ? 0 : Integer.MAX_VALUE;
|
|
|
|
|
}
|
|
|
|
|
int p1 = f1(arr, i + 1);
|
|
|
|
|
change1(arr, i);
|
|
|
|
|
int p2 = f1(arr, i + 1);
|
|
|
|
|
change1(arr, i);
|
|
|
|
|
p2 = (p2 == Integer.MAX_VALUE) ? p2 : (p2 + 1);
|
|
|
|
|
return Math.min(p1, p2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void change1(int[] arr, int i) {
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
arr[0] ^= 1;
|
|
|
|
|
arr[1] ^= 1;
|
|
|
|
|
} else if (i == arr.length - 1) {
|
|
|
|
|
arr[i - 1] ^= 1;
|
|
|
|
|
arr[i] ^= 1;
|
|
|
|
|
} else {
|
|
|
|
|
arr[i - 1] ^= 1;
|
|
|
|
|
arr[i] ^= 1;
|
|
|
|
|
arr[i + 1] ^= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean valid(int[] arr) {
|
|
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
|
|
if (arr[i] == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 无环改灯问题的递归版本
|
|
|
|
|
public static int noLoopMinStep1(int[] arr) {
|
|
|
|
|
if (arr == null || arr.length == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 1) {
|
|
|
|
|
return arr[0] ^ 1;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 2) {
|
|
|
|
|
return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
// 不变0位置的状态
|
|
|
|
|
int p1 = process1(arr, 2, arr[0], arr[1]);
|
|
|
|
|
// 改变0位置的状态
|
|
|
|
|
int p2 = process1(arr, 2, arr[0] ^ 1, arr[1] ^ 1);
|
|
|
|
|
if (p2 != Integer.MAX_VALUE) {
|
|
|
|
|
p2++;
|
|
|
|
|
}
|
|
|
|
|
return Math.min(p1, p2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当前在哪个位置上,做选择,nextIndex - 1 = cur ,当前!
|
|
|
|
|
// cur - 1 preStatus
|
|
|
|
|
// cur curStatus
|
|
|
|
|
// 0....cur-2 全亮的!
|
|
|
|
|
public static int process1(int[] arr, int nextIndex, int preStatus, int curStatus) {
|
|
|
|
|
if (nextIndex == arr.length) { // 当前来到最后一个开关的位置
|
|
|
|
|
return preStatus != curStatus ? (Integer.MAX_VALUE) : (curStatus ^ 1);
|
|
|
|
|
}
|
|
|
|
|
// 没到最后一个按钮呢!
|
|
|
|
|
// i < arr.length
|
|
|
|
|
if (preStatus == 0) { // 一定要改变
|
|
|
|
|
curStatus ^= 1;
|
|
|
|
|
int cur = arr[nextIndex] ^ 1;
|
|
|
|
|
int next = process1(arr, nextIndex + 1, curStatus, cur);
|
|
|
|
|
return next == Integer.MAX_VALUE ? next : (next + 1);
|
|
|
|
|
} else { // 一定不能改变
|
|
|
|
|
return process1(arr, nextIndex + 1, curStatus, arr[nextIndex]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 无环改灯问题的迭代版本
|
|
|
|
|
public static int noLoopMinStep2(int[] arr) {
|
|
|
|
|
if (arr == null || arr.length == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 1) {
|
|
|
|
|
return arr[0] == 1 ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 2) {
|
|
|
|
|
return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
int p1 = traceNoLoop(arr, arr[0], arr[1]);
|
|
|
|
|
int p2 = traceNoLoop(arr, arr[0] ^ 1, arr[1] ^ 1);
|
|
|
|
|
p2 = (p2 == Integer.MAX_VALUE) ? p2 : (p2 + 1);
|
|
|
|
|
return Math.min(p1, p2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int traceNoLoop(int[] arr, int preStatus, int curStatus) {
|
|
|
|
|
int i = 2;
|
|
|
|
|
int op = 0;
|
|
|
|
|
while (i != arr.length) {
|
|
|
|
|
if (preStatus == 0) {
|
|
|
|
|
op++;
|
|
|
|
|
preStatus = curStatus ^ 1;
|
|
|
|
|
curStatus = arr[i++] ^ 1;
|
|
|
|
|
} else {
|
|
|
|
|
preStatus = curStatus;
|
|
|
|
|
curStatus = arr[i++];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (preStatus != curStatus) ? Integer.MAX_VALUE : (op + (curStatus ^ 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 有环改灯问题的暴力版本
|
|
|
|
|
public static int loopRight(int[] arr) {
|
|
|
|
|
if (arr == null || arr.length == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 1) {
|
|
|
|
|
return arr[0] == 1 ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 2) {
|
|
|
|
|
return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
return f2(arr, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int f2(int[] arr, int i) {
|
|
|
|
|
if (i == arr.length) {
|
|
|
|
|
return valid(arr) ? 0 : Integer.MAX_VALUE;
|
|
|
|
|
}
|
|
|
|
|
int p1 = f2(arr, i + 1);
|
|
|
|
|
change2(arr, i);
|
|
|
|
|
int p2 = f2(arr, i + 1);
|
|
|
|
|
change2(arr, i);
|
|
|
|
|
p2 = (p2 == Integer.MAX_VALUE) ? p2 : (p2 + 1);
|
|
|
|
|
return Math.min(p1, p2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void change2(int[] arr, int i) {
|
|
|
|
|
arr[lastIndex(i, arr.length)] ^= 1;
|
|
|
|
|
arr[i] ^= 1;
|
|
|
|
|
arr[nextIndex(i, arr.length)] ^= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int lastIndex(int i, int N) {
|
|
|
|
|
return i == 0 ? (N - 1) : (i - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int nextIndex(int i, int N) {
|
|
|
|
|
return i == N - 1 ? 0 : (i + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 有环改灯问题的递归版本
|
|
|
|
|
public static int loopMinStep1(int[] arr) {
|
|
|
|
|
if (arr == null || arr.length == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 1) {
|
|
|
|
|
return arr[0] == 1 ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 2) {
|
|
|
|
|
return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 3) {
|
|
|
|
|
return (arr[0] != arr[1] || arr[0] != arr[2]) ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
// 0不变,1不变
|
|
|
|
|
int p1 = process2(arr, 3, arr[1], arr[2], arr[arr.length - 1], arr[0]);
|
|
|
|
|
// 0改变,1不变
|
|
|
|
|
int p2 = process2(arr, 3, arr[1] ^ 1, arr[2], arr[arr.length - 1] ^ 1, arr[0] ^ 1);
|
|
|
|
|
// 0不变,1改变
|
|
|
|
|
int p3 = process2(arr, 3, arr[1] ^ 1, arr[2] ^ 1, arr[arr.length - 1], arr[0] ^ 1);
|
|
|
|
|
// 0改变,1改变
|
|
|
|
|
int p4 = process2(arr, 3, arr[1], arr[2] ^ 1, arr[arr.length - 1] ^ 1, arr[0]);
|
|
|
|
|
p2 = p2 != Integer.MAX_VALUE ? (p2 + 1) : p2;
|
|
|
|
|
p3 = p3 != Integer.MAX_VALUE ? (p3 + 1) : p3;
|
|
|
|
|
p4 = p4 != Integer.MAX_VALUE ? (p4 + 2) : p4;
|
|
|
|
|
return Math.min(Math.min(p1, p2), Math.min(p3, p4));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 下一个位置是,nextIndex
|
|
|
|
|
// 当前位置是,nextIndex - 1 -> curIndex
|
|
|
|
|
// 上一个位置是, nextIndex - 2 -> preIndex preStatus
|
|
|
|
|
// 当前位置是,nextIndex - 1, curStatus
|
|
|
|
|
// endStatus, N-1位置的状态
|
|
|
|
|
// firstStatus, 0位置的状态
|
|
|
|
|
// 返回,让所有灯都亮,至少按下几次按钮
|
|
|
|
|
|
|
|
|
|
// 当前来到的位置(nextIndex - 1),一定不能是1!至少从2开始
|
|
|
|
|
// nextIndex >= 3
|
|
|
|
|
public static int process2(int[] arr,
|
|
|
|
|
int nextIndex, int preStatus, int curStatus,
|
|
|
|
|
int endStatus, int firstStatus) {
|
|
|
|
|
|
|
|
|
|
if (nextIndex == arr.length) { // 最后一按钮!
|
|
|
|
|
return (endStatus != firstStatus || endStatus != preStatus) ? Integer.MAX_VALUE : (endStatus ^ 1);
|
|
|
|
|
}
|
|
|
|
|
// 当前位置,nextIndex - 1
|
|
|
|
|
// 当前的状态,叫curStatus
|
|
|
|
|
// 如果不按下按钮,下一步的preStatus, curStatus
|
|
|
|
|
// 如果按下按钮,下一步的preStatus, curStatus ^ 1
|
|
|
|
|
// 如果不按下按钮,下一步的curStatus, arr[nextIndex]
|
|
|
|
|
// 如果按下按钮,下一步的curStatus, arr[nextIndex] ^ 1
|
|
|
|
|
int noNextPreStatus = 0;
|
|
|
|
|
int yesNextPreStatus = 0;
|
|
|
|
|
int noNextCurStatus =0;
|
|
|
|
|
int yesNextCurStatus = 0;
|
|
|
|
|
int noEndStatus = 0;
|
|
|
|
|
int yesEndStatus = 0;
|
|
|
|
|
if(nextIndex < arr.length - 1) {// 当前没来到N-2位置
|
|
|
|
|
noNextPreStatus = curStatus;
|
|
|
|
|
yesNextPreStatus = curStatus ^ 1;
|
|
|
|
|
noNextCurStatus = arr[nextIndex];
|
|
|
|
|
yesNextCurStatus = arr[nextIndex] ^ 1;
|
|
|
|
|
} else if(nextIndex == arr.length - 1) {// 当前来到的就是N-2位置
|
|
|
|
|
noNextPreStatus = curStatus;
|
|
|
|
|
yesNextPreStatus = curStatus ^ 1;
|
|
|
|
|
noNextCurStatus = endStatus;
|
|
|
|
|
yesNextCurStatus = endStatus ^ 1;
|
|
|
|
|
noEndStatus = endStatus;
|
|
|
|
|
yesEndStatus = endStatus ^ 1;
|
|
|
|
|
}
|
|
|
|
|
if(preStatus == 0) {
|
|
|
|
|
int next = process2(arr, nextIndex + 1, yesNextPreStatus, yesNextCurStatus,
|
|
|
|
|
nextIndex == arr.length - 1 ? yesEndStatus : endStatus, firstStatus);
|
|
|
|
|
return next == Integer.MAX_VALUE ? next : (next + 1);
|
|
|
|
|
}else {
|
|
|
|
|
return process2(arr, nextIndex + 1, noNextPreStatus, noNextCurStatus,
|
|
|
|
|
nextIndex == arr.length - 1 ? noEndStatus : endStatus, firstStatus);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// int curStay = (nextIndex == arr.length - 1) ? endStatus : arr[nextIndex];
|
|
|
|
|
// int curChange = (nextIndex == arr.length - 1) ? (endStatus ^ 1) : (arr[nextIndex] ^ 1);
|
|
|
|
|
// int endChange = (nextIndex == arr.length - 1) ? curChange : endStatus;
|
|
|
|
|
// if (preStatus == 0) {
|
|
|
|
|
// int next = process2(arr, nextIndex + 1, curStatus ^ 1, curChange, endChange, firstStatus);
|
|
|
|
|
// return next == Integer.MAX_VALUE ? next : (next + 1);
|
|
|
|
|
// } else {
|
|
|
|
|
// return process2(arr, nextIndex + 1, curStatus, curStay, endStatus, firstStatus);
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 有环改灯问题的迭代版本
|
|
|
|
|
public static int loopMinStep2(int[] arr) {
|
|
|
|
|
if (arr == null || arr.length == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 1) {
|
|
|
|
|
return arr[0] == 1 ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 2) {
|
|
|
|
|
return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
if (arr.length == 3) {
|
|
|
|
|
return (arr[0] != arr[1] || arr[0] != arr[2]) ? Integer.MAX_VALUE : (arr[0] ^ 1);
|
|
|
|
|
}
|
|
|
|
|
// 0不变,1不变
|
|
|
|
|
int p1 = traceLoop(arr, arr[1], arr[2], arr[arr.length - 1], arr[0]);
|
|
|
|
|
// 0改变,1不变
|
|
|
|
|
int p2 = traceLoop(arr, arr[1] ^ 1, arr[2], arr[arr.length - 1] ^ 1, arr[0] ^ 1);
|
|
|
|
|
// 0不变,1改变
|
|
|
|
|
int p3 = traceLoop(arr, arr[1] ^ 1, arr[2] ^ 1, arr[arr.length - 1], arr[0] ^ 1);
|
|
|
|
|
// 0改变,1改变
|
|
|
|
|
int p4 = traceLoop(arr, arr[1], arr[2] ^ 1, arr[arr.length - 1] ^ 1, arr[0]);
|
|
|
|
|
p2 = p2 != Integer.MAX_VALUE ? (p2 + 1) : p2;
|
|
|
|
|
p3 = p3 != Integer.MAX_VALUE ? (p3 + 1) : p3;
|
|
|
|
|
p4 = p4 != Integer.MAX_VALUE ? (p4 + 2) : p4;
|
|
|
|
|
return Math.min(Math.min(p1, p2), Math.min(p3, p4));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int traceLoop(int[] arr, int preStatus, int curStatus, int endStatus, int firstStatus) {
|
|
|
|
|
int i = 3;
|
|
|
|
|
int op = 0;
|
|
|
|
|
while (i < arr.length - 1) {
|
|
|
|
|
if (preStatus == 0) {
|
|
|
|
|
op++;
|
|
|
|
|
preStatus = curStatus ^ 1;
|
|
|
|
|
curStatus = (arr[i++] ^ 1);
|
|
|
|
|
} else {
|
|
|
|
|
preStatus = curStatus;
|
|
|
|
|
curStatus = arr[i++];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (preStatus == 0) {
|
|
|
|
|
op++;
|
|
|
|
|
preStatus = curStatus ^ 1;
|
|
|
|
|
endStatus ^= 1;
|
|
|
|
|
curStatus = endStatus;
|
|
|
|
|
} else {
|
|
|
|
|
preStatus = curStatus;
|
|
|
|
|
curStatus = endStatus;
|
|
|
|
|
}
|
|
|
|
|
return (endStatus != firstStatus || endStatus != preStatus) ? Integer.MAX_VALUE : (op + (endStatus ^ 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成长度为len的随机数组,值只有0和1两种值
|
|
|
|
|
public static int[] randomArray(int len) {
|
|
|
|
|
int[] arr = new int[len];
|
|
|
|
|
for (int i = 0; i < arr.length; i++) {
|
|
|
|
|
arr[i] = (int) (Math.random() * 2);
|
|
|
|
|
}
|
|
|
|
|
return arr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
System.out.println("如果没有任何Oops打印,说明所有方法都正确");
|
|
|
|
|
System.out.println("test begin");
|
|
|
|
|
int testTime = 20000;
|
|
|
|
|
int lenMax = 12;
|
|
|
|
|
for (int i = 0; i < testTime; i++) {
|
|
|
|
|
int len = (int) (Math.random() * lenMax);
|
|
|
|
|
int[] arr = randomArray(len);
|
|
|
|
|
int ans1 = noLoopRight(arr);
|
|
|
|
|
int ans2 = noLoopMinStep1(arr);
|
|
|
|
|
int ans3 = noLoopMinStep2(arr);
|
|
|
|
|
if (ans1 != ans2 || ans1 != ans3) {
|
|
|
|
|
System.out.println("1 Oops!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < testTime; i++) {
|
|
|
|
|
int len = (int) (Math.random() * lenMax);
|
|
|
|
|
int[] arr = randomArray(len);
|
|
|
|
|
int ans1 = loopRight(arr);
|
|
|
|
|
int ans2 = loopMinStep1(arr);
|
|
|
|
|
int ans3 = loopMinStep2(arr);
|
|
|
|
|
if (ans1 != ans2 || ans1 != ans3) {
|
|
|
|
|
System.out.println("2 Oops!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
System.out.println("test end");
|
|
|
|
|
|
|
|
|
|
int len = 100000000;
|
|
|
|
|
System.out.println("性能测试");
|
|
|
|
|
System.out.println("数组大小:" + len);
|
|
|
|
|
int[] arr = randomArray(len);
|
|
|
|
|
long start = 0;
|
|
|
|
|
long end = 0;
|
|
|
|
|
start = System.currentTimeMillis();
|
|
|
|
|
noLoopMinStep2(arr);
|
|
|
|
|
end = System.currentTimeMillis();
|
|
|
|
|
System.out.println("noLoopMinStep2 run time: " + (end - start) + "(ms)");
|
|
|
|
|
|
|
|
|
|
start = System.currentTimeMillis();
|
|
|
|
|
loopMinStep2(arr);
|
|
|
|
|
end = System.currentTimeMillis();
|
|
|
|
|
System.out.println("loopMinStep2 run time: " + (end - start) + "(ms)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|