目录
1.绳子最多覆盖的点
思考:想要覆盖最多的点,绳子的左端点或者右端点必须要正好在数组的点上(其他情况不可能超过这种情况),这样减少了很多种比较情况。
//固定右端点,二分查找左端点
private static int method1(int[] arr, int length) {
if (arr.length==0)
return 0;
//
int j=0;
int i=0;
int max =1;
for ( j = 1; j <arr.length; j++) {
//找到arr中第一个大于等于temp的下标
int temp = arr[j]-length;
int index=getFirst(arr,i,j,temp);
int l = j-index+1;
if (l>max)
max=l;
i=index;
}
return max;
}
private static int getFirst(int[] arr, int i, int j,int temp) {
while (i<j)
{
int mid = (i+j)/2;
if (arr[mid]==temp)
{
return mid;
}
else if (arr[mid]<temp)
{
i=mid+1;
}
else {
j=mid-1;
}
}
return arr[i]<temp?i+1:i;
}
采用不定长的滑动数组固定左端点,滑动右端点,若右端点超出长度时滑动左端点
// 采用不定长的滑动窗口
private static int method2(int[] arr, int length) {
if (arr.length==0)
return 0;
int j=1;
int i=0;
int max =1;
while(j<arr.length)
{
if (arr[j]-arr[i]<=length)
{
int l = j-i+1;
if (l>max)
max=l;
j++;
}else{
while (arr[j]-arr[i]>length)
{
i++;
}
}
}
return max;
}
2.最少装苹果袋数
思考:要想袋子数量最少,需要尽量使用容量为8的袋子,假设装到最后剩余n个,此时需要减少容量为8的袋子数量,使得8x+n=6y,x为减少的袋子,y为增加的袋子,x从0到2,当x=3时3*8=4*6,相当于0-2再进行循环,所以只用判定8x+n<24的情况
private static int method(int num) {
int i = num/8;
int m = num%8;
if (m==0)
return i;
int j= 0;
for (int k = 1; k < 3; k++) {
if ((k*8+m)%6==0)
{
j = (k*8+m)/6;
return i-k+j;
}
}
return -1;
}
3.牛羊吃草问题
两头牛每次只能吃4的n次方份草,当有n份草时,先吃和后吃那个能赢?
思考:两头牛在吃草时只能选择吃4的n次方份草,其逻辑是一样的,可以采用递归的方法,当先手牛吃完草后,后手牛吃草,后手牛也要赢,此时在代码逻辑上,后手牛便成为了先手牛,原来的先手牛成为了后手牛;先手的牛具有主动权,若发现在某一条路上后手牛一定输,则直接向上返回先手牛赢,此时的先手牛是子过程的后手牛,所以先手牛接收到是后手牛赢,而只有先手牛怎末都赢不了的时候后手牛才赢,递归退出条件为n=0,当然也可以向后多算几个退出条件。
//两头牛调用同样代码,谁调用谁为代码意义上的先手牛
private static String winner(int n) {
if (n<5)
{
return (n==0||n==2)?"后手":"先手";
}
int base = 1;
while(base<=n)
{
//先手有主动权,先手赢一种就赢了,此时它为子过程的后手牛
//若后手赢,由于先手有主动权,先手可不选这条路所以此时先手不一定赢,不返回
if (winner(n-base).equals("后手"))
return "先手";
if (base>n/4)
{
break;
}
base*=4;
}
//先手没有一种情况可以赢,此时后手赢
return "后手";
}
4.预处理问题(左G右R)
给定一个只有‘G’,‘R’的字符数组,要求涂成G只存在R的左边的,求涂的最少次数
思路:遍历数组,在遍历时分别求两边需要更改的数量,时间复杂度为O(n)。在遍历时时间很多用于求左边的‘R’和右边的‘G’的数量,显然我们可以通过预处理事先准备好数据,在视频中老师采用数组存储数量,但其实也可以采用几个变量存储,只需在遍历中不断更新即可。
private static int minPaint(char[] chars) {
//总共g的个数
int gCount = 0;
// 指针左边r的个数
int temp=0;
for (int i = 0; i < chars.length; i++) {
if (chars[i]=='G')
gCount++;
}
//r全染成g的情况,for循环未包含这种情况
int min = chars.length-gCount;
// 从i指针向右全为r
for (int i = 0; i < chars.length; i++) {
// 左边r的个数+右边g的个数(总个数-左边g的个数)
int sum = temp+gCount-(i-temp);
if (sum<min)
min=sum;
if (chars[i]=='R')
temp++;
}
return min;
}
5.预处理问题(1139. 最大的以 1 为边界的正方形)
思路:如果在矩阵中选取一个长方形,任取对角线两点,每次可能性都为O(n的平方),重复长方形的不影响最终复杂的,所以时间复杂度是n的四次方,在选择正方形时对角线有限制,最终时间复杂度为n的三次方,在确定正方形后,遍历正方形也需要O(n)的时间复杂度,在遍历边时,可能不同的正方形都要遍历相同的一段边,所以考虑能否采用预处理简化步骤。
在确定一个左上顶点后,我们如果知道其向右、向下连续为1的长度,以及知道对应的右上顶点连续向下连续为1的长度的长度,左下顶点向右连续为1的长度,那我们就可以不用遍历只需要逻辑判断就可以了。
所以我们需要两个矩阵记录顶点分别向右、向下连续为1的长度,以计算向右长度为例,若从左向右计算逻辑复杂,所以采用从后向前计算。
public int largest1BorderedSquare(int[][] matrix) {
int length = matrix[0].length;
int height = matrix.length;
int[][] right = new int[height][length];
int[][] down = new int[height][length];
for (int i = 0; i < height; i++) {
for (int j = length-1; j >=0 ; j--) {
if(j==length-1&&matrix[i][j]==1)
right[i][j]=1;
else {
if(matrix[i][j]==1)
right[i][j]=1+right[i][j+1];
}
}
}
for (int i = 0; i < length; i++) {
for (int j =height-1; j >=0 ; j--) {
if(j==height-1&&matrix[j][i]==1)
down[j][i]=1;
else {
if(matrix[j][i]==1)
down[j][i]=1+down[j+1][i];
}
}
}
int r =0;
for (int i = 0; i < height; i++) {
for (int j = 0; j < length; j++) {
//倒着取,若大的满足小的就不用找了
for (int k =Math.min(right[i][j],down[i][j]); k>=1;k--) {
if(k<=r)
break;
if(right[i+k-1][j]>=k&&down[i][j+k-1]>=k)
{
r=k;
break;
}
}
}
}
return r*r;
}
6.二进制01生成器
思考:(1)由1-5等概率生成,制作01生成器,可以1-2为0,4-5为1,为3试重新生成;1-7站三位二进制,可以让随机生成的1和0组成三位二进制数,为零时重新生成。
(2)p概率返回0,1-p概率返回1,则返回10和01的概率都为p(1-p),分别作为1和0返回的条件
public static int f(){
return (int)(Math.random()*5)+1;
}
public static int r1(){
int a;
do {
a = f();
}while(a==3);
return a<3?0:1;
}
//1-7 等概率返回
public static int r2()
{
int a;
do{
a = (r1()<<2)+(r1()<<1)+r1();
}while (a==0);
return a;
}
//非等概率返回0/1
public static int r3(){
int a=f();
return a<2?0:1;
}
// 非等概率0/1返回 等概率0/1
public static int r4()
{
int a;
do{
a = (r3()<<1)+r3();
}while (a==0||a==3);
return a==1?0:1;
}