[92双周赛]题解与总结

112 阅读5分钟

前言

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

参加周赛也有了一段时间了,奈何,成绩每况愈下,一发不可收拾,总结一下。

题目

第一题

链接:6249. 分割圆的最少切割次数 - 力扣(LeetCode)

题解:

是我唯一作对的一道🤦‍♀️

其实思路还是很明确的,因为他所有切割都是经过圆点的

  • 假如切出来是偶数,那么直接按照直径去切就可以了

  • 假如是奇数,那么就按照半径切,有几次切几次

  • 但是注意1的情况

代码

/**
 * @param {number} n
 * @return {number}
 */
var numberOfCuts = function(n:number) {
//     如果n为奇数
    if(n%2){
        if(n === 1){
            return 0
        }
        return n
    }else{
        return n/2
    }
};

第二题

链接:6277. 行和列中一和零的差值 - 力扣(LeetCode)

最初的想法:

  • 因为题目要求,判断0和1在各行各列的插值

  • 所以我就可以计算出,每一行和列有多少个1

  • 然后row+col减去1的行列总数,得到0的行列总数

  • 判断两个的差值

代码:

/**
 * @param {number[][]} grid
 * @return {number[][]}
 */

// 通过异或判断
var xo = (num) => {
    let count = 0;
    while (num) {
        if (num & 1) {
            count++
        }
        num = num >> 1;
    }
    return count
}
var onesMinusZeros = function (grid) {
    //这一行和列1的个数-这一行和列0的个数
    let col = grid[0].length;
    let row = grid.length;
    //记录行里面多少个1
    let countRow = []
    //记录列里面多少个1
    let countCol = []
    let res = []
    //计算一行里有多少个1
    for (let value of grid) {
        // let num = parseInt(value.join("")).toString(2)
        let num = Number.parseInt(value.join(""),2)
        console.log("行num",num)
        countRow.push(xo(num))
    }
    console.log("行的1",countRow)

    // 计算一列里面有多少个1
    for (let i = 0; i < col; i++){
        let num = "";
        for (let j = 0; j < row; j++){
            num += grid[j][i];
        }
        console.log("列num",num)
        countCol.push(xo(parseInt(num,2)))
    }
    console.log("列的1",countCol)
    for (let i = 0; i < row; i++){
        let temp = []
        for (let j = 0; j < col; j++){
            let count1 = countRow[i] + countCol[j];
            let count0 = row+col - count1;
            temp.push(count1-count0)
        }
        res.push(temp)
    }
    return res
};

总结:

  • 其实思路并没有什么问题
  • 但是问题在于,数据量太大了,太多的for循环,消耗量太多的时间
  • 而且,找不到比较好的,获取一个列的数据的方法

新的思路:

观看了一位UP主的视频,【力扣双周赛 92】前后缀分解_哔哩哔哩_bilibili

  • 这是一道模拟题
  • 他将diff[i][j] = onesRowi + onesColj - zerosRowi - zerosColj
  • 理解为了diff[i][j] = onesRowi - zerosRowi + onesColj - zerosColj
  • 其实他是在计算一行和一列里10的差是多少
  • 巧妙的点在于,他是将1变为1,将0变为-1
  • 这样的话,-1+1=0,多出来没有消掉的就是,差的值
  • 下次,我们只需要将当前Row+Col,就可以知道总的差是多少了

代码:

/**
 * @param {number[][]} grid
 * @return {number[][]}
 */
var onesMinusZeros = function (grid:number[][]) {
    //这一行和列1的个数-这一行和列0的个数
    let Row = Array<number>(grid.length).fill(0)
    let Col = Array<number>(grid[0].length).fill(0);
    for( const [i,row] of grid.entries()){
        // i就是下标,从0开始,一直到grid.length
        // row其实是一个数组,表示每一行的数
        for(const [j,value] of row.entries()){
            // j也是下标,表示grid[0].length,value表示的是,当前这个值
            Row[i] += value*2-1;//不使用判断,将1变为1,0变为-1
            Col[j] += value*2-1;
        }
    }
    console.log("col",Col)
    console.log("row",Row)
    // 现在这个数组,是由1和-1组成的
    // 对这个数组进行遍历
    for(const [i,x] of Row.entries()){
        for(const [j,y] of Col.entries()){
            grid[i][j] = x+y;
        }
    }
    return grid
};

第三题

链接:6250. 商店的最少代价 - 力扣(LeetCode)

想法:

我第一题大概花了7分钟写完,害,磨磨唧唧的。

第二题,因为总觉得自己没写错,有想不到更好的方法,基本耗时有一个小时左右,🤷‍♀️。

给到第三题的时间也只有40分钟左右,后来题目都没看明白,代价分为哪几个部分

思路:

  1. 首先必须明确,在i时间关门的代价是
    • 代价i = i之前N的数量+i之后Y的数量
  2. 所以我们可以实现一个函数来计算Y的个数,这样N也就可以知道
  3. 然后比较,最小时间

代码:

function bestClosingTime(customers: string): number {
    // 首先我们需要明确的是
    // i的代价 = 在i之前的N的数量和在i之后的Y的数量
    // 首先我们需要一个函数,来计算当前有多少个Y 
    // 首先计算有多少个Y
    let SumY = countY(customers); 
    let str = "";
    // let res = [SumY]
    let min = SumY;
    let minIndex = 0;
    for(let i = 0;i<customers.length;i++){
        str += customers[i]
        let leftY = countY(str)
        let leftN = (i+1)-leftY

        let rightY = SumY-leftY
        // res.push(leftN+rightY)
        if(min > leftN+rightY){
            minIndex = i+1;
            min = leftN+rightY
        }
    }
    return minIndex
    
};
const countY = (openTime:string):number=>{
    let count = 0;
    for(let value of openTime){
        if(value === "Y"){
            count++
        }
    }
    return count
}
// const findMin = (arr:number[]):number=>{
//     let min = arr[0];
//     let index = 0;
//     for(let [i,value] of arr.entries()){
//         if(min > value){
//             min = value;
//             index = i
//         }
//     }
//     return index
// }

起初,我是打算用数组来承接所有的代价,然后找出最早的最小的,但是超时了

现在,我直接用一个min变量直接承接,但是,还是超时🤦‍♀️

累了

---------11.29更新一下---------------

看了UP主的解题视频,非常巧妙,直接将复杂度降到了O(n)

他是这么分析的

  • 首先在0位置关门的话,那么我的代价其实就是,所有的Y的个数
  • 那么我假设最小的代价min_count,就是在第0个位置产生
  • 然后向后遍历
    • 假如我遇到了Y
      • 那么也就是,刚好在这个时候,有顾客到来,那么代价count1
      • 这时候就可能会出现最小代价,那么判断,更新数据
      • 因为需要返回下标,所以,我们需要一个变量index来承接下标
    • 假如我遇到了N
      • 也就是,我开门的时候,没有顾客到来
      • 那么代价+1
      • 也就不可能出现最小代价min_count
  • 注意:这边的下标其实需要+1,这是因为,因为遍历是从0开始的,但是,题目其实是从-1开始的,所以,需要+1

代码:

function bestClosingTime(customers: string): number {
    // 首先我们需要明确的是
    // i的代价 = 在i之前的N的数量和在i之后的Y的数量
    // 首先我们需要一个函数,来计算当前有多少个Y 
    // 首先计算有多少个Y
    let SumY = countY(customers); 
    // 因为要求最小值,所以维护一个最小值
    let count = SumY;
    let min_count = count;
    // 设置一个下标,到时候输出答案
    let index = 0;
    for(let i = 0;i<customers.length;i++){
        if(customers[i] === "Y"){
            // 那就说明,现在是有顾客的,并且门也是开着的
            // 代价-1
            count--;
            // 这时候可能产生的最小代价,那么随之而来的是,下标也要变化
            // min_count = Math.min(min_count,count);
            if(count <min_count){
                index = i+1;
                min_count = count
            }
        }else{
            // 如果当前是N,也就是关门
            // 那么也就是,我开店的时候,没有顾客来,那么代价加1
            count++
            // 因为count++,也不可能出现,最小count,所以,后面无需判断
        }
    }
    return index
};
const countY = (openTime:string):number=>{
    let count = 0;
    for(let value of openTime){
        if(value === "Y"){
            count++
        }
    }
    return count
}

第四题

困难题,我现在不强求自己会,先把基本题目看对再说吧

总结

  • 对模拟的题目不是很了解,需要多多练习

  • 思路打不开,一些方法比较巧妙,想不到啊