蓝桥杯 2023 年国赛题解(三)

May 12 · 12min

Tip

建议先刷题,再看题解😎!

恶龙与公主

题目: 恶龙与公主

第一个问要求我们将一个二维数组以顺时针螺旋的形式输出。这其实是一个伪算法题,因为它不是走迷宫的题目,路径是固定的。

参考下图:

从左上角开始,按照顺时针螺旋的顺序,依次输出每个位置的值。这里给出一个方法:可以按顺序(上、右、下、左)地删除二维数组的元素,直到二维数组为空。

function mazePath(arr) {
  const newArr = []
  while (arr.length) {
    arr.length && newArr.push(...(arr.shift()))
    arr.length && newArr.push(...arr.map(e => e.pop()))
    arr.length && newArr.push(...(arr.pop()).reverse())
    arr.length && newArr.push(...arr.map(e => e.shift()).reverse())
  }
  return newArr
}
  • 上:从二维数组中拿出第一个数组(shift()),并将其元素依次放入数组中。
  • 右:从二维数组中拿出每个数组的最后一个元素(pop()),并将其依次放入数组中。
  • 下:从二维数组中拿出最后一个数组(pop()),并将其元素逆序(reverse())依次放入数组中。
  • 左:从二维数组中拿出每个数组的第一个元素(shift()),并将其逆序(reverse())依次放入数组中。

这里要注意的是,这个二维数组不一定在哪一步被清空(取决于二维数组的大小),所以每个操作前都需要确保二维数组不为空,再进行操作。

第二问没有什么特殊的考点,直接给出:

moveHandler() {
    let step = this.element.gameStep.value = this.getStep(this.gameData.step);
    // TODO:根据点击营救后获得的点数,正确到达指定位置调用bloodCalculator函数计算当前血量,到达公主处调用 tipRender 函数,每步的时间间隔在 200ms内(大于此时间会导致判题失败)。
    this.gameData.curPos += step

    document.querySelector('.container>.active').classList.remove('active')

    if (this.gameData.curPos >= this.gameData.pathArr.length - 1) {
        this.gameData.curPos = this.gameData.pathArr.length - 1
        this.tipRender("success")
    }

    document.querySelector(`[data-index="${this.gameData.pathArr[this.gameData.curPos]}"]`).classList.add('active')

    this.bloodCalculator(document.querySelector('.container>.active'))
    if (this.gameData.curBlood == 0) this.tipRender("warning")
},

可以关注一下选择器的使用,我们之前讲过>表示直接子元素,空格表示后代元素;+表示相邻紧跟的兄弟元素,~表示所有兄弟元素。

版本比较器

题目: 版本比较器

function compareVersion(version1, version2) {
  // TODO:待补充代码
  let ver1 = version1.split('.').map(Number).filter(it => it >= 0 && it <= 9)
  let ver2 = version2.split('.').map(Number).filter(it => it >= 0 && it <= 9)
  if (ver1.length !== 3 || ver2.length !== 3) {
    return 'error'
  }
  for (let i = 0; i < 3; i++) {
    if (ver1[i] < ver2[i]) {
      return -1
    }
    else if (ver1[i] > ver2[i]) {
      return 1
    }
    else {
      continue // 当然这行没什么意义,只是为了更清晰
    }
  }
  return 0
}

这题本身是一道非常简单的题,而且也有非常清晰的测试用例,基本上测试用例通过了也就拿到分了。这里有几个需要注意的点:

  • 1.1.21.1.10谁大?题干中明确强调了是按照数值进行比较,而不是按照 ASCII 字典序进行比较,所以在将两个字符串分割成数组后,一定要记得将它们转成整数。

  • 怎么筛选出error的情况?在我的解法中,我直接将字符串转成数值,再判断其每项是否在0-9之间,过滤掉错误的项;如果有项被过滤掉,其数组长度一定小于3,所以会返回error。当然,你也可以直接对原字符串用正则判断,这肯定更优雅:

let regex = /^\d+\.\d+\.\d+$/ // 注意`.`的转义
if (!regex.test(version1) || !regex.test(version2)) {
  return 'error'
}

图片验证码

题目: 图片验证码

这是一道非常麻烦的题,非常考验你对 JavaScript 函数式编程的理解;而且考验了细心程度,可能考虑得不够周到就会痛失这道题的分数。

第一问点击图片,为图片添加样式,并根据选中情况修改按钮中的文字。这题我一开始是使用原生 DOM 的classList操作来添加或移除样式:

function chooseImg(index) {
  let containers = [...document.querySelectorAll(".image-container")]
  let checkBtn = document.getElementById("check-btn")
  containers[index].classList.toggle("selected")
  // 重置为未选中状态
  this.images[index].type = containers[index].classList.contains("selected")
  if (this.images.some(img => img.type)) {
    checkBtn.textContent = "提交"
  } else {
    checkBtn.textContent = "跳过"
  }
}

实际上,Vue 是数据驱动的,这么写并不符合 Vue 的风格。所以你可以使用 Vue 中的条件绑定样式语法::class="{ selected: image.type }",仅在image.typetrue时添加selected类。

接着有一个问题,怎么把选中图片的信息记录下来呢?现在有两个变量imagesallImagesimages表示的是从allImages中随机选出的 9 个图片对象;图片对象中有type属性,表示的是该图片是否为正确答案。

我想修改一下images变量,使其type表示的含义是该图片是否被选中而不是是否为正确答案。但是要注意:imagesallImages中对图片对象都保持引用关系,如果改变其中一个变量中图片对象的type,会影响另一个变量;所以我们可以深克隆一下images,再修改克隆后的type

this.images = structuredClone(
  pick(this.allImages.filter(i => i.category === this.category), 9)
).map(img => img.type = false, img)

最后一个map将所有图片对象的type属性都设置为false(因为默认都是未选中状态)。structuredClone是 ES 新增的 API,用于深克隆一个对象。新增这个方法之前,如果我们不手动编写方法,就只能用JSON.parse(JSON.stringify(obj))来深克隆一个对象,但是这个方法有局限性,比如不能克隆函数、undefinedSymbol等。

当然,这个方法未必是最好的,你可以另起一个属性,比如selected,来表示图片是否被选中。这个方法就不用深克隆对象了。

第二问我们重点看下检查结果的逻辑:

let backups = this.allImages
  .filter(it => it.category === this.category) // 其实不筛选也行,路径对应唯一的类别
let result = this.images.every(img => {
  // allImages 变量的 type 表示正确与否,images变量的 type 表示选中与否
  return backups.find(backup => backup.path === img.path).type === img.type
})

注意我们要对images进行遍历判断,仅当答案(用findallImages中查找)与当前图片的选中状态全部相同时,才返回真。


至此题解讲完了 2023 年国赛大学组和职业组的全部题目,接下来会更新 2024 年和 2022 年的,欢迎与我交流!


>