左神中级提升班

目录

 1.绳子最多覆盖的点

2.最少装苹果袋数

3.牛羊吃草问题

4.预处理问题(左G右R)

5.预处理问题(1139. 最大的以 1 为边界的正方形)

6.二进制01生成器


 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;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值