蓝桥杯Web2022年第十三届蓝桥杯Web大学组国赛真题解析

Posted 海底烧烤店ai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝桥杯Web2022年第十三届蓝桥杯Web大学组国赛真题解析相关的知识,希望对你有一定的参考价值。

前言

省赛真题解析见:
2022年第十三届蓝桥杯Web大学组省赛真题解析(完整版)
2022年第十三届蓝桥杯Web大学组省赛真题解析(精华版)

更多蓝桥杯题解请查阅专栏:蓝桥杯

之前写省赛解析时篇幅过长,写的花里胡哨的,导致文章阅读体验不好,这次就不整那些了,直接贴代码,解析都写在代码注释里了,相信各位老大稍微思考一下就能够理解了,同样的,相应的真题代码也会分享给大家:

「蓝桥杯」https://www.aliyundrive.com/s/7fsobhSy8dZ 提取码: 34pi

如果在阅读文章时大佬有好的见解,或者发现了问题,还请多多留言,互相探讨

文章目录

1、分一分(5分)

/**
 * @param Object oldArr
 * @param Object num
 * */
const splitArray = (oldArr, num) => 
    // TODO:请补充代码实现功能
    let newArr = [];
    
    // 升序排序后解构赋值深拷贝给oldArr2
    let oldArr2 = [...oldArr.sort((a, b) => a - b)];
    
    const len = oldArr2.length;
    for (let i = 0, j = 0; i < len; i += num, j++) 
        // splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
        newArr[j] = oldArr2.splice(0, num);
    

    return newArr;
;
module.exports = splitArray; // 检测需要,请勿删除

2、新鲜的蔬菜(5 分)

/* TODO:待补充代码 */

#box1 
    display: flex;
    justify-content: center;
    align-items: center;


#box2,
#box3 
    display: flex;
    flex-direction: column;
    justify-content: space-between;


#box2 span:nth-child(2),
#box3 span:nth-child(3) 
    align-self: flex-end;


#box3 span:nth-child(2) 
    align-self: center;

3、水果消消乐(10分)

// TODO:请补充代码
function startGame() 
    $("img").show(500);
    $("#start").hide();
    $("img").hide(500);
    // 已经点击的数量
    let i = 0;
    // 存放已经点击的元素
    let clickImg = [];

    [...$(".img-box")].forEach((item) => 
        item.onclick = function () 
            i++;
            if (i <= 2) 
                $(item.children).show();
                clickImg.push(item);

                if (i === 2) 
                    setTimeout(() => 
                        let score = $("#score")[0];

                        if (
                            clickImg[0].children[0].alt ===
                            clickImg[1].children[0].alt
                        ) 
                            score.innerhtml = Number(score.innerHTML) + 2;
                            $(clickImg[0]).css(
                                // 隐藏元素且保留元素所占位置
                                visibility: "hidden",
                            );
                            $(clickImg[1]).css(
                                visibility: "hidden",
                            );
                         else 
                            score.innerHTML = Number(score.innerHTML) - 2;
                            $(clickImg[0].children[0]).hide();
                            $(clickImg[1].children[0]).hide();
                        
                        clickImg = [];
                        i = 0;
                    , 400);
                
            
        ;
    );


4、用什么来做计算A (10分)

// TODO:请补充代码
const btn = document.getElementsByClassName("calc-button");
const formula = document.getElementById("formula");
const result = document.getElementById("result");

// 计算机表达式展示
let showText = "";
// 计算结果
let num = "";

[...btn].forEach((item) => 
    item.onclick = function () 
        switch (this.id) 
            // 点击 =
            case "equal":
                // eval() 函数会将传入的字符串当做 javascript 代码进行执行。
                num = eval(showText.replace("x", "*").replace("÷", "/"));
                result.value = num;
                return;
            // 点击 √
            case "sqrt":
                num = eval(showText.replace("x", "*").replace("÷", "/"));
                num = Math.sqrt(num);
                result.value = num;
                return;
            // 点击 AC
            case "reset":
                showText = "";
                num = "";
                formula.value = showText;
                result.value = num;
                return;
            default:
                showText += this.innerHTML;
                formula.value = showText;
                return;
        
    ;
);

5、开学礼物大放送(15 分)

考察的只是简答的页面实现,每个人的实现方式不同,代码差异也很大,这里就不放代码了

6、权限管理(15 分)

$(function () 
    // 使用 ajax 获取 userList.json 数据并渲染到页面
    getData();

    // 为按钮添加事件
    $("#add").click(function () 
        // TODO:补充代码,实现功能

        // 获取选中的option
        let option = $("#leftSelect option:selected");

        // jQ方法:each() 遍历jQ获取的节点
        option.each((index, item) => 
            // 删除左侧对应的option
            $(`#leftSelect option[value=$item.value]`).remove();
            // 向右侧添加option
            $("#rightSelect")[0].add(new Option(item.value, item.value));
        );

        changeAccess("管理员", option);
    );

    $("#addAll").click(function () 
        // TODO:补充代码,实现功能
        let option = $("#leftSelect option");
        option.each((index, item) => 
            $(`#leftSelect option[value=$item.value]`).remove();
            $("#rightSelect")[0].add(new Option(item.value, item.value));
        );
        changeAccess("管理员", option);
    );

    $("#remove").click(function () 
        // TODO:补充代码,实现功能
        let option = $("#rightSelect option:selected");
        option.each((index, item) => 
            $(`#rightSelect option[value=$item.value]`).remove();
            $("#leftSelect")[0].add(new Option(item.value, item.value));
        );
        changeAccess("普通用户", option);
    );
    $("#removeAll").click(function () 
        // TODO:补充代码,实现功能
        let option = $("#rightSelect option");
        option.each((index, item) => 
            $(`#rightSelect option[value=$item.value]`).remove();
            $("#leftSelect")[0].add(new Option(item.value, item.value));
        );
        changeAccess("普通用户", option);
    );
);

/**
 * 修改权限
 * @param Object right 要修改的权限
 * @param Object changeList 要修改权限的用户列表
 */
function changeAccess(right, changeList) 
    // TODO:补充代码,实现功能
    changeList.each((index, item) => 
        // 将option.value与tr.name对应,找到对应的td并修改其内容
        // jQ方法::last 获取最后个元素
        $(`#userList tr[name=$item.value] td:last`).html(right);
    );

// 异步获取数据
function getData() 
    // TODO:补充代码,实现功能
    $.ajax("./js/userList.json").then((res) => 
        res.forEach((item) => 
            // jQ方法:html() 设置html内容
            $("#userList tbody").html(
                $("#userList tbody").html() +
                    ` <tr name=$item.name>
                         <td>$item.name</td>
                         <td>$item.right ? "管理员" : "普通用户"</td> 
                      </tr>`
            );
        );
    );


7、一起会议吧(20分)

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>一起会议吧</title>
    <link rel="stylesheet" type="text/css" href="./css/index.css" />
    <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
</head>

<body>
    <div id="app">
        <!-- TODO:请在下面实现需求 -->
        <!-- 登录/注销窗口 -->
        <div class="login">
            <div class="left-tools">
                <a class="close-btn"></a>
                <a class="shrink-btn"></a>
            </div>
            <h3>isLogin?'注销':'登录'</h3>
            <p v-if="!isLogin">
                选择用户:<select id="selectUser" @change="changeOption($event)">
                    <option :value="item.id" v-for="item in list" :key="item.id">item.name</option>
                </select>
            </p>
            <p v-else>当前用户为:loginUser.name</p>

            <a class="login-btn" @click="btn">isLogin?'注销':'登录'</a>
        </div>

        <!-- 右侧显示用户列表窗口按钮 -->
        <button id="show" class="right-btn" v-if="!showUser&&isLogin" @click="showUser=true">
            <span class="iconfont icon-left-arrow"></span>
        </button>

        <!-- 用户列表窗口 -->
        <div class="user-dialog" v-if="isLogin&&showUser">
            <!-- 用户列表窗口上侧工具栏 -->
            <ul class="tools">
                <li class="tools-left">
                    <button :class="'active':isButton<0" @click="isButton=-1">
                        <span class="iconfont icon-close"></span>
                    </button>
                    <button :class="'active':isButton=='0'" @click="isButton=0">
                        <span class="iconfont icon-dialog"></span>
                    </button>
                    <button :class="'active':isButton>0" @click="isButton=1">
                        <span class="iconfont icon-list"></span>
                    </button>
                </li>
                <li class="tools-right">
                    <button class="show-list" @click="showUser=false">
                        <span class="iconfont icon-retract"></span>
                    </button>
                </li>
            </ul>

            <!-- 用户列表 -->
            <ul class="say-list">
                <li>
                    <span class="iconfont icon-microphone"></span>
                </li>
                <li class="line"></li>
                <li>正在讲话:list.find(item=>item.isHost).name</li>
            </ul>
            <ul class="user-list">
                <li v-show="isButton>=0">
                    <img class="header" :src="loginUser.imgPath" />
                    <div class="user-name">
                        <span class="iconfont icon-user header-icon" v-if="loginUser.isHost">第1题 —— 九进制转十进制 (5分)
  • 第2题 —— 顺子日期 (5分)
  • 第3题 —— 刷题统计 (10分)
  • 第4题 —— 修剪灌木 (10分)
  • 第5题 —— X进制减法 (15分)
  • 第6题 —— 统计子矩阵 (15分)
  • 第7题 —— 积木画 (20分)
  • 第8题 —— 扫雷 (20分)
  • 第9题 —— 李白打酒加强版 (25分)
  • 第10题 —— 砍竹子 (25分)
  • 补题链接:地址

    第1题 —— 九进制转十进制 (5分)

    • 进制转换,9的0次方乘2+9的1次方乘2+2次方乘0+三次方乘9,输出就行。
    • 答案:1478
    #include <iostream>
    using namespace std;
    int main()
      cout<<2+2*9+2*9*9*9<<"\\n";
      return 0;
    
    

    第2题 —— 顺子日期 (5分)

    • 年份确定了是2022,可以枚举出每一天,然后暴力判断一下就行。
    • 答案:14
    #include<bits/stdc++.h>
    using namespace std;
    int days[13] =  0,31,28,31,30,31,30,31,31,30,31,30,31 ; //2022不是闰年
    int main()
        int cnt = 0;
        for(int month = 1; month <= 12; month++)
        for (int day = 1; day <= days[month]; day++) 
            string s = "2022";
            if (month < 10) s += '0'+ to_string(month);
            else s += to_string(month);
            if (day < 10) s += '0' + to_string(day);
            else s += to_string(day);
            if(s.find("012") != s.npos || s.find("123") != s.npos)
                cnt++;
            
        
        cout<<cnt<<"\\n";
        return 0;
    
    

    第3题 —— 刷题统计 (10分)

    • 不难想到暴力模拟,day++表示天数,然后不断减掉对应星期几的天数即可。
    • 1e18会超时,因为每周的题数是固定的,所以单独拿出来做除法。
    • 1e18要开longlong。
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL; //开ll
    int main()
        LL a, b, n;  cin>>a>>b>>n;
        LL c = a*5+b*2; //每周做的题数
        LL day = n/c*7; //直接除
        n = n%c;
        while(n > 0)
            day++;
            if(day%7==6 || day%7==0)n -= b; //周末
            else n -= a;
        
        cout<<day<<"\\n";
        return 0;
    
    
    

    第4题 —— 修剪灌木 (10分)

    • 题意:给一个数组,每秒钟所有数+1,指针开始在最左边,每秒往右移动一个,到边界后转向回来然后往复循环,求数组可能产生的最大值。
    • 其实一眼结论题
      每个灌木被剪掉以后,在下一次被剪之前,这段时间内,灌木丛会生长到最大,就是看他什么下次什么时候被剪。
      所以当前灌木丛的最大长度取决于灌木丛到两端点的距离,那么for一遍输出就行了。
    #include<bits/stdc++.h>
    using namespace std;
    int main()
        int n;  cin>>n;
        for(int i = 1; i <= n; i++)cout<<max(i-1,n-i)*2<<endl;
        return 0;
    
    
    

    第5题 —— X进制减法 (15分)


    • 题意:定义X进制表示每一位进制不同的数。给出两个X进制数A和B(规则相同,但是不知道规则,且最高N进制,最低2进制),求A-B的可能最小值。

    • 思路:
      因为AB的规则是一样的,所以A-B第i位的值其实已经确定了,就是给他们加个规则。
      那必然是进制越小越好,最好是2进制。
      如果都是2进制就不满足规则了,所以最小进制肯定是右边i+1上A和B的最大+1,不要再大了。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int mod = 1000000007, maxn = 1e5+10;
    LL a[maxn], b[maxn];
    
    int main()
        int n, an, bn;  cin>>n>>an;
        for(int i = an; i >= 1; i--)cin>>a[i];//1-n低位到高位
        cin>>bn;
        for(int i = bn; i >= 1; i--)cin>>b[i];
        LL ans = 0, base = 1;
        for(int i = 1; i <= an; i++)
            LL w = max(a[i], b[i],1LL)+1;
            ans = (ans+(a[i]-b[i])*base)%mod;
            base = base*w%mod;
        
        cout<<ans<<"\\n";
        return 0;
    
    
    

    第6题 —— 统计子矩阵 (15分)

    • 题意:求给出的nm的矩阵里有多少个子矩阵满足和不超过k。
    • 不难想到二维前缀和,然后暴力枚举对角线,四次方可以求出所有的解。
      但是数据范围是500的,所以还要再优化一维
    • 不难想到枚举一个横坐标上的区间以后,纵坐标维度上的可以采用双指针(尺取法)来做
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn = 550;
    LL a[maxn][maxn], b[maxn][maxn], ans;
    
    int main()
        int n, m, k;  cin>>n>>m>>k;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                cin>>a[i][j];
                a[i][j] += a[i-1][j]+a[i][j-1]-a[i-1][j-1];//二维前缀和
            
        
        for(int l = 1; l <= m; l++)
            for(int r = l; r <= m; r++)
                for(int i = 1, j = 1; i <= n; i++)   //双指针
                    while(j <= i && (a[i][r]-a[i][l-1]-a[j-1][r]+a[j-1][l-1])>k)
                        j++;
                    
                    if(j <= i)ans += i-j+1;
                
            
        
        cout<<ans<<"\\n";
        return 0;
    
    
    

    第7题 —— 积木画 (20分)


    • 题意:有2种积木,2xn的板子,求有多少放法。
    • 明显是个dp,想一下状态,令f(i,j) 表示前i-1列填满且第i列的状态为j,j=0(00) j=1(10) j=2(01) j=3(11)时候的放法数量。线性+2维的复杂度也是刚刚好。
    • 然后转移的时候尝试把各种格子放进去即可。
    • 这题一个比较坑的点是直接开longlong会爆内存,要int然后转longlong,内存卡的有点紧。
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn = 1e7+10, mod = 1e9+7;
    int f[maxn][4];
    
    int main()
        int n;  cin>>n;
        f[0][3] = 1;
        f[1][0] = 1; f[1][1] = 1; f[1][2] = 1; f[1][3] = 2;
        for(int i = 2; i <= n; i++)
            f[i][0]=(LL)(f[i-1][3])%mod;
            f[i][1]=(LL)(f[i-1][0]+f[i-1][2])%mod;
            f[i][2]=(LL)(f[i-1][1]+f[i-1][0])%mod;
            f[i][3]=((LL)(f[i-1][1]+f[i-1][2])%mod+(LL)(f[i-1][3]+f[i-2][3])%mod)%mod;
        
        cout<<f[n][0]<<endl;
        return 0;
    
    
    

    第8题 —— 扫雷 (20分)


    • 题意:给出n个地雷,坐标x,y和半径r的圆。接下来m个排雷火箭,也是坐标x,y和半径r的圆。排雷火箭范围内的地雷会爆炸,然后爆炸的地雷范围内的地雷也会爆炸,求最后炸了多少个地雷。
    • 注意到所有点爆炸半径都不超过10,而且每个点坐标都是整数,那么对于一个点其半径内的点完全可以枚举出来,最多也就400个点,n是5e4,乘起来相当于遍历一遍所有可能的点,复杂度也够。
    • 坐标直接用map存的话带个log会超时,所以要用无向的map(加个哈希)
    #include<bits/stdc++.h>
    using namespace std;
    inline long long qpow(long long x) return x * x; 
    
    struct hsh 
        size_t operator () (const pair<int,int> &a) const 
            return a.first * 239 + a.second * 7; 
        
    ;
    unordered_map< pair<int,int>, int, hsh> mp;
    unordered_map< pair<int,int>, int, hsh> rr;
    long long ans = 0;
    
    void dfs(int x, int y, int r)
        for(int i = x-r; i <= x+r; i++)  //枚举半径范围内的所有点
            for(int j = y-r; j <= y+r; j++)
                if(qpow(i-x)+qpow(j-y) <= qpow(r))
                    auto it = make_pair(i,j);
                    if(mp.count(it)) //判断是不是雷
                        ans += mp[it];
                        mp.erase(it);
                        dfs(i,j,rr[it]); //递归爆炸
                    
                
            
        
    
    
    int main()
        ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
        int n, m;  cin>>n>>m;
        for(int i = 1; i <= n; i++)
            int x, y, r;  cin>>x>>y>>r;
            auto pos = make_pair(x,y);
            mp[pos]++;
            rr[pos] = max(r, rr[pos]);
        
        while(m--)
            int x, y, r;  
            cin>>x>>y>>r;
            dfs(x, y, r); //从这里开始爆炸
        
        cout<<ans<<"\\n";
        return 0;
    
    
    
    
    

    第9题 —— 李白打酒加强版 (25分)

    • 题意:开始有2斗酒, 逢店加一倍, 遇花喝一斗。一共遇到店 N 次, 遇到花 M 次。已知最后一次遇到的是花, 他正好把酒喝光了。求一路遇到店和花的顺序, 有多少种不同的可能?
      没酒 ( 0 斗) 时遇店是合法的, 加倍后还是没酒; 但是没酒时遇 花是不合法的。
    • 明显也是dp,比积木还简单一点,据说是有原题。令 f[i][j][k] 表示走过了 i 个酒馆,j 个花,还有 k 斗酒的方案数, 此时复杂度刚刚好。
    • 转移:到酒馆时,因为到酒馆时翻一倍,所以这时的 k 一定是偶数。遇到花时,上一次的酒比当前多 1,所以从k+1转移。
    • 可以用记忆化写,看起来清楚一点。
    #include<bits/stdc++.h>
    using namespace std;
    int n, m,dp[110][110][110];
    
    int dfs(int i, int j, int k)//走过了i个酒馆,j个花,还有k斗酒的方案数
        if(i<0 || j<0 || k<0)return 0;//遇花,遇店,酒壶里的酒都要>=0
        if(i>k)return 0; //每次要喝1,所以酒馆要小于总酒量
        if(j==0 && i==1 && k==1)return 1; //边界状态,一个酒馆
        if(dp[i][j][k] != -1)return dp[i][j][k];
        return dp[i][j][k] = (dfs(2*i,j-1,k)+dfs(i-1,j,k-1))%1000000007;
    
    
    int main()
        memset(dp, -1, sizeof(dp));
        cin>>n>>m;
        cout<<dfs(2,n,m)<<"\\n";
        return 0;
    
    
    
    

    第10题 —— 砍竹子 (25分)


    • 题意:一排n个竹子,每次可以选一个高度相等的区间变成根号hi/2+1下取整,求最少多少次能让所有竹子的高度变为1。
    • 数据范围 2e5,暴力肯定过不去,估计nlogn。因为是根号,所以把一根竹子变成1最多只要操作6次,所以到时候肯定会有很多相等的区间的竹子,我们不妨贪心的从高到低砍竹子(这样肯定不会更坏)。
    • 此时不难想到用堆维护,我们每次找出最高的竹子,把相同高度且编号连续的竹子依次取出(所以要重载结构体维护一个编号),然后把竹子砍一半再加入堆中。直到最高的竹子的高度是1就代表我们已经结束了。
    • 用longlong的时候,sqrt要换成sqrtl!!不然会爆精度,其他类似的函数也是。

    总结2点:
    1、首先我们注意到,一般的做题过程就是,找到某个结论(关键点,突破口),然后做优化的那种题目,比如说数据范围1e5,但是某个数据很小(发现6次就砍完,地雷范围r<10),就可以拿这个做文章,再比如一眼暴力,然后想想怎么用结论(比如说来回走动的规律,到两边的距离,双指针)这种来优化,或者说其实大多数dp类型的思维/暴力题,都有点这种意味在里面。
    2、另外一个思维的切入点是,先想清楚再写代码,think twice, code once,这样不容易思路被带偏,不要上来就啪啪啪输入输出,这样后面就卡住了(起码我是这样的,会被打断,可能题意都忘记了,那还写个锤子),大体思路想好,感觉方案可行了,优化完了再写,细节具体再补充上去就行,不要上来就暴力,不是每道题都有写暴力对拍的时间成本的。 虽然在写不出来了的时候多交一个暴力骗分也是一个好习惯。

    #include<bits/stdc++.h>
    using namespace std;
    typedef 2022年第十三届蓝桥杯web开发—东奥大抽奖题目附官方解答

    第十三届蓝桥杯c++b组国赛题解(还在持续更新中...)

    2022年第十三届蓝桥杯比赛Java B组 全部真题答案解析-第一部分

    2022年第十三届蓝桥杯B组 试题 E: 求阶乘

    第十三届蓝桥杯Java B 组国赛 C 题——左移右移(AC)

    2022年第十三届蓝桥杯省赛Java B组真题及题解

    (c)2006-2019 SYSTEM All Rights Reserved IT常识