【计蒜客模拟赛系列】-计蒜客2021年8月普及组模拟赛

提前:本文中部分代码和思路有借鉴或摘抄计蒜客官方题解

赛后总结

本次模拟赛的难度总算正常了些
个人战绩: 220/400,排名61 ,太弱了,一大堆AK爷
题目质量评价: 题目相比CSP-J还是简单了一些,比较友好,题目质量还是比较高的。个人觉得前两题题面写的非常长,但是提炼出来重点后,就会觉得非常简单,很锻炼大家的信息提取与概括能力;代码实现难度低,平均也就个三四十行左右。
考查知识点: T1、2模拟,T3动态规划,T4堆 (没考图和树算法?)

题解

T1 鲜奶贩卖

题目链接: https://nanti.jisuanke.com/t/56597
涉及知识点: 模拟
解题思路:
签到题。
直接根据题意降价并判断即可。
正解代码:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int x, h, y; //开始1000元,保质期h小时,预算y元 
bool f = false; //能不能买到

int main() {
    freopen("milk.in", "r", stdin);
    freopen("milk.out", "w", stdout);
    cin >> x >> h >> y;
    if (y >= x) {
        f = true;
        cout << 0 << endl;
    } else {
        for (int i = 1; i < h; i++) {
            x -= ceil(1.0 * i / 10);
            if (y >= x) {
                f = true;
                cout << i << endl;
                return 0;
            }
        }
    }
    if (!f) {
        cout << "IMPOSSIBLE" << endl;
    }
    return 0;
}

T2 滑冰游戏

题目链接: https://nanti.jisuanke.com/t/56598
涉及知识点: 模拟
解题思路:
相比T1,又增添了几分难度。
题面很长,概况一下:给定一个字符串为指定操作,可能向上、下、左、右移动,碰到'#'代表的墙壁或者'.'代表的冰块就停下。总分加上停下的这块方格的数值。也有可能打碎障碍冰块,冰块的方向一定是上一次操作的指定方向,且与人物相邻,打碎后原来冰块的地方分数为0
正解代码:
考场代码,可能有些冗杂,当然是可以优化的

#include <iostream>
#include <cstdio>
using namespace std;

//上下左右 敲碎
//U D L R  B
int n, m, x, y;
long long ans;
string opr;
char map[25][25];

int main() {
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> map[i][j];
        }
    }
    cin >> x >> y >> m >> opr;
    for (int i = 0; i < m; i++) {
        if (opr[i] == 'B') { //按照上一次的操作来敲碎冰块
            if (opr[i - 1] == 'U') map[x - 1][y] = '0';
            if (opr[i - 1] == 'D') map[x + 1][y] = '0';
            if (opr[i - 1] == 'L') map[x][y - 1] = '0';
            if (opr[i - 1] == 'R') map[x][y + 1] = '0';
        }
        int mx = x, my = y; //用来探索的
        if (opr[i] == 'U') { //向上
            while (map[mx - 1][my] != '*' && map[mx - 1][my] != '#') {
                mx--;
            }
            ans += map[mx][my] - '0';
            x = mx, y = my;
            map[mx][my] = '0'; //将停留处清零
        }
        if (opr[i] == 'D') { //向下
            while (map[mx + 1][my] != '*' && map[mx + 1][my] != '#') {
                mx++;
            }
            ans += map[mx][my] - '0';
            x = mx, y = my;
            map[mx][my] = '0';
        }
        if (opr[i] == 'L') { //向左
            while (map[mx][my - 1] != '*' && map[mx][my - 1] != '#') {
                my--;
            }
            ans += map[mx][my] - '0';
            x = mx, y = my;
            map[mx][my] = '0';
        }
        if (opr[i] == 'R') { //向右
            while (map[mx][my + 1] != '*' && map[mx][my + 1] != '#') {
                my++;
            }
            ans += map[mx][my] - '0';
            x = mx, y = my;
            map[mx][my] = '0';
        }
    }
    cout << ans << endl;
    return 0;
}

T3 小明与春娇叠积木

考试时这题竟然爆零了,丢脸
题目链接: https://nanti.jisuanke.com/t/56599
涉及知识点: 动态规划
解题思路:
分档考虑。
30pts:
这个子任务给了我们一个条件,那就是他们俩的积木是相同的,也就是说我们可以只操作一个。
第一种写法,是暴力枚举计算积木最高高度,这里就不多说了。
第二种写法,是动态规划,设dp[i]是以i结尾的最高高度核心部分给出:

    for (int i = 1; i <= n; i++) {
        dp[i] = 1; //初始化
        for (int j = 1; j < i; j++) {
            if (a[j] + 1 == a[i] && dp[i] < dp[j] + 1) { //如果可以连得上并且dp[i]在更新后会更大
                dp[i] = dp[j] + 1;
            }
        }
        if (dp[i] > ans) {
            ans = dp[i]; //更新答案
        }
    }

60pts:
其实如果我们能够写出30pts的dp,我们就能照葫芦画瓢地对两座积木分别执行一样的dp操作。当然也可以用贪心做,我们设定i是一个序列的起点,如果枚举中与a[j]一样就把now/now2加1,最后得出的就是

for (int i = 1; i <= n; i++) {
    int now = i, now2 = i;
    for (int j = 1; j <= n; j++) {
        if (a[j] == now) {
            now++;
        }
        if (b[j] == now2) {
            now2++;
        }
    }
    if (ans < min(now, now2) - i) { //自己拿一组小数据模拟一下就知道了
        ans = min(now, now2) - i;
    }
}

100pts:
以动态规划的形式,计算两组积木的最高高度。
dp[i]为以i结尾的最高高度,则状态转移方程为dp[i] = max(dp[i], dp[i - 1] + 1)

T4 苹果售卖

题目链接: https://nanti.jisuanke.com/t/56642
涉及知识点: 暴力即枚举、正解即堆
解题思路:
现在是2021年8月29日23:34分,我想睡觉了,明天再继续更新
正解代码:
明天写

#include <iostream>
#include <cstdio>
using namespace std;

const int MAXN = 2 * 1e5 + 5;
int n, ans;
int dp[2][MAXN];

void make_dp (int dp[], int n) {
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        dp[x] = max(dp[x], dp[x - 1] + 1);
    }
    return;
}
int main() {
    freopen("juggle.in", "r", stdin);
    freopen("juggle.out", "w", stdout);
    cin >> n;
    make_dp(dp[0], n);
    make_dp(dp[1], n);
    for (int i = 1; i <= n; i++) {
        ans = max(ans, min(dp[0][i], dp[1][i]));
    }
    cout << ans << endl;
    return 0;
}

反思

1.T1、2这种简单题尽量能15min内解决,也就是说基础算法我必须熟记于心,说写就写(达成)
2.对于动态规划和后面一堆奇奇怪怪的知识点本蒟蒻还在不断学习中,未来也是必须要熟练的,那么在2021CSP中我就要选择拿尽可能多的部分分
2021CSP-J++!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oier_Asad.Chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值