package class03;
import org.junit.Test;
public class study230724 {
//1.滑动窗口的例题:
// 给定一个有序数组arr,从左到右依次表示一个点,现在有一个长度len的绳子,求绳子最大覆盖的点数
public int qes1(int[] arr, int len) {
//思路1:将绳子的右端固定在每个点上,找到arr[i]-L的第一个点index,一直滑动
int res = 0;
for (int R = 0; R < arr.length; R++) {
int m = arr[R] - len;
int j = 0;
while (arr[j] < m) {
j++;
}
res = Math.max(res, R - j + 1);
}
return res;
}
//打表法
//对于一个整数n,满足n=i*6+8*j (i,j)均为整数,返回i+j的最小值,不满足返回-1
public int qes2(int n) {
//分析n=i*6+8*j => n = 2(3*i+4*j) => n是偶数,才能存在这样的i,j
if ((n & 1) != 0) {
return -1;
}
int m = n / 8;
while (m >= 0) {
int i = n - 8 * m;
if (i > 25) {
return -1;
}
if (i % 6 == 0) {
return m + i / 6;
}
m--;
}
return m;
}
//方法2:整数输入和整数输出 可以考虑打表法,分析发现从3开始,每轮都有4个数带上-1,8个为一轮
//-1 -1 -1 -1 -1 1
//-1 1 -1 -1 -1 2
//-1 2 -1 2 -1
// 3 -1 3 -1 3 -1 3 -1
// 4 -1 4 -1 4 -1 4 -1 26
// 5 -1 5 -1 5 -1 5 -1 34
// 6 -1 6 -1 6 -1 6 -1
// 7 -1 7 -1 7 -1 7 -1
// 8 -1 8
//-1 8 -1 8 -1 9
//-1 9 -1 9 -1 9
//-1 10 -1 10 -1 10
//-1 10 -1 11 -1 11
//-1 11 -1 11 -1 12
//-1 12 -1 12 -1 12
//-1 13 -1 13
//Process finished with exit code 0
public int qes3(int n) {
if ((n & 1) != 0) {
return -1;
}
if (n >= 18) {
return (n - 18) / 8 + 3;
} else {
if (n == 6 || n == 8) {
return 1;
} else if (n == 12 || n == 14 || n == 16) {
return 2;
}
}
return -1;
}
//打表法2:对于一个数n,有两个人A,B先后拿走4的i次方的数字,拿走最后一个数的人会赢,
//对于n,问先后谁赢?
//小贪心算法,如果A拿走 m = 4的i(0~log n)可以赢,那就拿走m; 不能赢,再去考虑拿走i++次方的数
//经发现实际上就是A B A A B循环,直接打表计算即可
public String qes5(int n) {
if (n == 0) {
return "B";
}
if (n == 1) {
return "A";
}
int i = 1;
while (i <= n) {
if (qes5(n - i).equals("B")) { //子过程中表示后手 实际上是母过程的先手
return "A";
}
if (i > n / 4) {
break;
}
i = i * 4;
}
return "B";
}
//预处理技巧
//问题6:一排正方形进行染红色和绿色,原始正方形已经已存在颜色,比如RGGRRGG
//染色要求是:绿色G的左边不能有红色R,求最少的染色次数能够达到要求
public int qes6(String str) {
int res = str.length();
//思路:G的左边不能存在R表示G的左边只能有G=>GGGGRRRR结构
//定义左右的分界线i(0~n-1) [0~i)是左边G,[i~n-1]是右边R
//判断左边有多少R个数,右边有多少G的个数,之和就是i变更的染色次数,
//判断i的最小
for (int i = 0; i < str.length(); i++) {
int Rcount = 0;
for (int j = 0; j < i; j++) {
if (str.charAt(j) == 'R')
Rcount++;
}
int Gcount = 0;
for (int j = i; j < str.length(); j++) {
if (str.charAt(j) == 'G')
Gcount++;
}
res = Math.min(Rcount + Gcount, res);
}
return res;
}
//对于问题6 ,子循环可以采用预处理技巧,利用空间换时间,增加两个数组A B
//A从左到右统计R的数量,B从右到左统计G的数量,从而减少时间复杂度
public int qes6_1(String str) {
int len = str.length();
int res = len;
int[] Rcount = new int[len];
Rcount[0] = str.charAt(0) == 'R' ? 1 : 0;
for (int i = 1; i < len; i++) {
Rcount[i] = str.charAt(i) == 'R' ? Rcount[i - 1] + 1 : Rcount[i - 1];
}
int[] Gcount = new int[len];
Gcount[len - 1] = str.charAt(len - 1) == 'G' ? 1 : 0;
for (int i = len - 2; i >= 0; i--) {
Gcount[i] = str.charAt(i) == 'G' ? Gcount[i + 1] + 1 : Gcount[i + 1];
} //预处理i的左右两端RG的数量,减少时间复杂度
for (int i = 0; i < len; i++) {
res = Math.min(Rcount[i] + Gcount[i] - 1, res); //
}
return res;
}
//预处理7,对于一个N*N的(0,1)组成的矩阵arr,求矩阵内边数全是1的最大正方形边长
//根据给定的代码,可能存在以下问题:
//1. 循环边界条件错误:在第一个for循环中,循环条件为 i < N ,应该改为 i < N-1 ,因为在内部循环中,会访问到 arr[i + k-1] ,如果 i 等于 N ,则会超出数组边界。
//2. 正方形边长计算错误:在第三个for循环中,循环条件为 l < j + k ,应该改为 l < j + k + 1 ,因为正方形的边长应该是 k+1 。
//3. 逻辑错误:在判断正方形的四个边是否都是1时,应该使用或运算符 || 而不是与运算符 && ,因为只要有一个边不是1,就应该跳出循环。
public int qes7(int[][] arr) {
//分析:在一个N*N的矩阵内,可以组成正方形有n的三次方可能
int res = 1;
//int M = arr.length;
int N = arr[0].length;
for (int i = 1; i < N; i++) {
for (int j = 1; j < N; j++) { //(i,j正方形左上角点坐标集合)
for (int k = 1; k < Math.min(N - i, N - j); k++) { //正方形边长
boolean flag = false;
//依次判断正方形的四个边是否都是1
for (int l = j; l < j + k; l++) {
if (arr[i - 1][l] != 1) {
flag = true;
break;
}
}
if (!flag) {
for (int l = j; l < j + k; l++) {
if (arr[i + k - 1][l] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
for (int l = i; l < i + k; l++) {
if (arr[l][j] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
for (int l = i; l < i + k; l++) {
if (arr[l][j + k] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
res = Math.max(res, k);
}
}
}
}
return res;
}
//修复后代码
public int qes7_2(int[][] arr) {
int res = 1;
int N = arr[0].length;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 1; k < Math.min(N - i, N - j); k++) {
boolean flag = false;
for (int l = j; l < j + k + 1; l++) {
if (arr[i][l] != 1 || arr[i + k][l] != 1) {
flag = true;
break;
}
}
if (!flag) {
for (int l = i; l < i + k + 1; l++) {
if (arr[l][j] != 1 || arr[l][j + k] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
res = Math.max(res, k + 1);
}
}
}
}
return res;
}
//在ques7_2中,出现大量的重复代码,并且在O(n4),那我们要考虑到减少时间复杂度,
//定义right[i][j]表示(i,j)坐标右边包含自己有多少个连续的1
//定义down[i][j]表示(i,j)坐标下边包含自己有多少个连续的1
public int qes7_3(int[][] arr) {
int res = 1;
int N = arr[0].length;
int[][] right = new int[N][N];
int[][] down = new int[N][N];
for (int i = 0; i < N; i++) {
right[i][N - 1] = arr[i][N - 1]; //处理边界
for (int j = N - 2; j >= 0; j--) {
right[i][j] = arr[i][j] == 0 ? 0 : right[i][j + 1] + 1;
}
}
for (int i = 0; i < N; i++) {
down[N - 1][i] = arr[N - 1][i]; //处理边界
for (int j = N - 2; j >= 0; j--) {
down[j][i] = arr[j][i] == 0 ? 0 : down[j + 1][i] + 1;
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 1; k < Math.min(N - i, N - j); k++) {
if (right[i][j] > k && down[i][j] > k && down[i][j + k] > k && right[i + k][j] > k) {
res = Math.max(res, k + 1);
}
}
}
}
return res;
}
@Test
public void Test() {
int[] ints = {1, 2, 4, 7, 9, 11, 15};
int i = qes1(ints, 6);
System.out.println("最大覆盖点数是" + i);
// System.out.println("打表法:");
// for (int j = 1; j <= 100; j++) {
// System.out.print(qes2(j) + " ");
// if (j % 6 == 0) {
// System.out.println();
// }
// }
// System.out.println();
// for (int j = 1; j < 101; j++) {
// System.out.print(qes5(j) + " ");
// if (j % 5 == 0) {
// System.out.println();
// }
// }
System.out.println();
System.out.println("最少染色次数");
long l1 = System.nanoTime();
String s = "GGGGGRGRGRGRGGRGRGGGGGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGRGGRGRGRRRRGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGRRRRGRGRGR";
int i1 = qes6(s);
System.out.println(i1);
long l2 = System.nanoTime();
System.out.println((l2 - l1) / 1000000 + "时间");
l1 = System.nanoTime();
int i2 = qes6_1(s);
System.out.println(i2);
l2 = System.nanoTime();
System.out.println((l2 - l1) / 1000000 + "时间");
//
System.out.println("边长最大");
int[][] ints1 = {
{0, 1, 1, 1, 1},
{0, 1, 0, 1, 1},
{0, 1, 1, 1, 1},
{0, 1, 1, 1, 1},
{0, 0, 0, 0, 0}
};
int i3 = qes7_2(ints1);
int i4 = qes7_3(ints1);
System.out.println(i3 + "==?" + i4);
}
}
package class03;
import org.junit.Test;
public class study230724 {
//1.滑动窗口的例题:
// 给定一个有序数组arr,从左到右依次表示一个点,现在有一个长度len的绳子,求绳子最大覆盖的点数
public int qes1(int[] arr, int len) {
//思路1:将绳子的右端固定在每个点上,找到arr[i]-L的第一个点index,一直滑动
int res = 0;
for (int R = 0; R < arr.length; R++) {
int m = arr[R] - len;
int j = 0;
while (arr[j] < m) {
j++;
}
res = Math.max(res, R - j + 1);
}
return res;
}
//打表法
//对于一个整数n,满足n=i*6+8*j (i,j)均为整数,返回i+j的最小值,不满足返回-1
public int qes2(int n) {
//分析n=i*6+8*j => n = 2(3*i+4*j) => n是偶数,才能存在这样的i,j
if ((n & 1) != 0) {
return -1;
}
int m = n / 8;
while (m >= 0) {
int i = n - 8 * m;
if (i > 25) {
return -1;
}
if (i % 6 == 0) {
return m + i / 6;
}
m--;
}
return m;
}
//方法2:整数输入和整数输出 可以考虑打表法,分析发现从3开始,每轮都有4个数带上-1,8个为一轮
//-1 -1 -1 -1 -1 1
//-1 1 -1 -1 -1 2
//-1 2 -1 2 -1
// 3 -1 3 -1 3 -1 3 -1
// 4 -1 4 -1 4 -1 4 -1 26
// 5 -1 5 -1 5 -1 5 -1 34
// 6 -1 6 -1 6 -1 6 -1
// 7 -1 7 -1 7 -1 7 -1
// 8 -1 8
//-1 8 -1 8 -1 9
//-1 9 -1 9 -1 9
//-1 10 -1 10 -1 10
//-1 10 -1 11 -1 11
//-1 11 -1 11 -1 12
//-1 12 -1 12 -1 12
//-1 13 -1 13
//Process finished with exit code 0
public int qes3(int n) {
if ((n & 1) != 0) {
return -1;
}
if (n >= 18) {
return (n - 18) / 8 + 3;
} else {
if (n == 6 || n == 8) {
return 1;
} else if (n == 12 || n == 14 || n == 16) {
return 2;
}
}
return -1;
}
//打表法2:对于一个数n,有两个人A,B先后拿走4的i次方的数字,拿走最后一个数的人会赢,
//对于n,问先后谁赢?
//小贪心算法,如果A拿走 m = 4的i(0~log n)可以赢,那就拿走m; 不能赢,再去考虑拿走i++次方的数
//经发现实际上就是A B A A B循环,直接打表计算即可
public String qes5(int n) {
if (n == 0) {
return "B";
}
if (n == 1) {
return "A";
}
int i = 1;
while (i <= n) {
if (qes5(n - i).equals("B")) { //子过程中表示后手 实际上是母过程的先手
return "A";
}
if (i > n / 4) {
break;
}
i = i * 4;
}
return "B";
}
//预处理技巧
//问题6:一排正方形进行染红色和绿色,原始正方形已经已存在颜色,比如RGGRRGG
//染色要求是:绿色G的左边不能有红色R,求最少的染色次数能够达到要求
public int qes6(String str) {
int res = str.length();
//思路:G的左边不能存在R表示G的左边只能有G=>GGGGRRRR结构
//定义左右的分界线i(0~n-1) [0~i)是左边G,[i~n-1]是右边R
//判断左边有多少R个数,右边有多少G的个数,之和就是i变更的染色次数,
//判断i的最小
for (int i = 0; i < str.length(); i++) {
int Rcount = 0;
for (int j = 0; j < i; j++) {
if (str.charAt(j) == 'R')
Rcount++;
}
int Gcount = 0;
for (int j = i; j < str.length(); j++) {
if (str.charAt(j) == 'G')
Gcount++;
}
res = Math.min(Rcount + Gcount, res);
}
return res;
}
//对于问题6 ,子循环可以采用预处理技巧,利用空间换时间,增加两个数组A B
//A从左到右统计R的数量,B从右到左统计G的数量,从而减少时间复杂度
public int qes6_1(String str) {
int len = str.length();
int res = len;
int[] Rcount = new int[len];
Rcount[0] = str.charAt(0) == 'R' ? 1 : 0;
for (int i = 1; i < len; i++) {
Rcount[i] = str.charAt(i) == 'R' ? Rcount[i - 1] + 1 : Rcount[i - 1];
}
int[] Gcount = new int[len];
Gcount[len - 1] = str.charAt(len - 1) == 'G' ? 1 : 0;
for (int i = len - 2; i >= 0; i--) {
Gcount[i] = str.charAt(i) == 'G' ? Gcount[i + 1] + 1 : Gcount[i + 1];
} //预处理i的左右两端RG的数量,减少时间复杂度
for (int i = 0; i < len; i++) {
res = Math.min(Rcount[i] + Gcount[i] - 1, res); //
}
return res;
}
//预处理7,对于一个N*N的(0,1)组成的矩阵arr,求矩阵内边数全是1的最大正方形边长
//根据给定的代码,可能存在以下问题:
//1. 循环边界条件错误:在第一个for循环中,循环条件为 i < N ,应该改为 i < N-1 ,因为在内部循环中,会访问到 arr[i + k-1] ,如果 i 等于 N ,则会超出数组边界。
//2. 正方形边长计算错误:在第三个for循环中,循环条件为 l < j + k ,应该改为 l < j + k + 1 ,因为正方形的边长应该是 k+1 。
//3. 逻辑错误:在判断正方形的四个边是否都是1时,应该使用或运算符 || 而不是与运算符 && ,因为只要有一个边不是1,就应该跳出循环。
public int qes7(int[][] arr) {
//分析:在一个N*N的矩阵内,可以组成正方形有n的三次方可能
int res = 1;
//int M = arr.length;
int N = arr[0].length;
for (int i = 1; i < N; i++) {
for (int j = 1; j < N; j++) { //(i,j正方形左上角点坐标集合)
for (int k = 1; k < Math.min(N - i, N - j); k++) { //正方形边长
boolean flag = false;
//依次判断正方形的四个边是否都是1
for (int l = j; l < j + k; l++) {
if (arr[i - 1][l] != 1) {
flag = true;
break;
}
}
if (!flag) {
for (int l = j; l < j + k; l++) {
if (arr[i + k - 1][l] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
for (int l = i; l < i + k; l++) {
if (arr[l][j] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
for (int l = i; l < i + k; l++) {
if (arr[l][j + k] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
res = Math.max(res, k);
}
}
}
}
return res;
}
//修复后代码
public int qes7_2(int[][] arr) {
int res = 1;
int N = arr[0].length;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 1; k < Math.min(N - i, N - j); k++) {
boolean flag = false;
for (int l = j; l < j + k + 1; l++) {
if (arr[i][l] != 1 || arr[i + k][l] != 1) {
flag = true;
break;
}
}
if (!flag) {
for (int l = i; l < i + k + 1; l++) {
if (arr[l][j] != 1 || arr[l][j + k] != 1) {
flag = true;
break;
}
}
}
if (!flag) {
res = Math.max(res, k + 1);
}
}
}
}
return res;
}
//在ques7_2中,出现大量的重复代码,并且在O(n4),那我们要考虑到减少时间复杂度,
//定义right[i][j]表示(i,j)坐标右边包含自己有多少个连续的1
//定义down[i][j]表示(i,j)坐标下边包含自己有多少个连续的1
public int qes7_3(int[][] arr) {
int res = 1;
int N = arr[0].length;
int[][] right = new int[N][N];
int[][] down = new int[N][N];
for (int i = 0; i < N; i++) {
right[i][N - 1] = arr[i][N - 1]; //处理边界
for (int j = N - 2; j >= 0; j--) {
right[i][j] = arr[i][j] == 0 ? 0 : right[i][j + 1] + 1;
}
}
for (int i = 0; i < N; i++) {
down[N - 1][i] = arr[N - 1][i]; //处理边界
for (int j = N - 2; j >= 0; j--) {
down[j][i] = arr[j][i] == 0 ? 0 : down[j + 1][i] + 1;
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 1; k < Math.min(N - i, N - j); k++) {
if (right[i][j] > k && down[i][j] > k && down[i][j + k] > k && right[i + k][j] > k) {
res = Math.max(res, k + 1);
}
}
}
}
return res;
}
@Test
public void Test() {
int[] ints = {1, 2, 4, 7, 9, 11, 15};
int i = qes1(ints, 6);
System.out.println("最大覆盖点数是" + i);
// System.out.println("打表法:");
// for (int j = 1; j <= 100; j++) {
// System.out.print(qes2(j) + " ");
// if (j % 6 == 0) {
// System.out.println();
// }
// }
// System.out.println();
// for (int j = 1; j < 101; j++) {
// System.out.print(qes5(j) + " ");
// if (j % 5 == 0) {
// System.out.println();
// }
// }
System.out.println();
System.out.println("最少染色次数");
long l1 = System.nanoTime();
String s = "GGGGGRGRGRGRGGRGRGGGGGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGRGGRGRGRRRRGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGGGGGRGRGRGRGGRGRGRRRRGRGRGRGRRRRGRGRGR";
int i1 = qes6(s);
System.out.println(i1);
long l2 = System.nanoTime();
System.out.println((l2 - l1) / 1000000 + "时间");
l1 = System.nanoTime();
int i2 = qes6_1(s);
System.out.println(i2);
l2 = System.nanoTime();
System.out.println((l2 - l1) / 1000000 + "时间");
//
System.out.println("边长最大");
int[][] ints1 = {
{0, 1, 1, 1, 1},
{0, 1, 0, 1, 1},
{0, 1, 1, 1, 1},
{0, 1, 1, 1, 1},
{0, 0, 0, 0, 0}
};
int i3 = qes7_2(ints1);
int i4 = qes7_3(ints1);
System.out.println(i3 + "==?" + i4);
}
}