湖南科技大学2022年大学生计算机程序设计新生赛题解

目录

前言:

A.Welcome Freshmen

难度:0颗星

B.鳖在这里发电

解题思路:

难度:1颗星

C.我的书馆

解题思路:

难度:3颗星

D.吃席

解题思路:

难度:2颗星

E.嘉嘉的序列游戏

解题思路:

难度:4颗星

F.情书

解题思路:

难度:4颗星

G.云顶之奕

解题思路:

难度:3颗星

H.夏季大三角

解题思路:

难度:5颗星

I.无限梦境的概率理论

难度:3颗星

方法一:

方法二:

J.交叉坐标的星尘

难度:6颗星

结语



前言:

--该题解以c语言代码为主,目的在于让代码更容易理解,同时部分题目加入少量c++内容,如果不能理解相应c++函数的可以上网学习一下,因为这里用到的函数都非常重要!

--为了能够比较清晰的了解题目难度的分布,将题目给予一定的star数,star数量越多越难;

--为了解释清楚,有的题解配了图片,纯手画图,比较潦草,大家理解便好。

A.Welcome Freshmen

签到,输入接输出级即可

难度:0颗星

#include <stdio.h>

int main()
{
    int a;
    scanf("%d", &a);
    if (a == 1)
        printf("freshman\n");
    else if (a == 2)
        printf("sophomore\n");
    else if (a == 3)
        printf("junior\n");
    else
        printf("senior\n");
    return 0;
}

B.鳖在这里发电

解题思路:

题意简单来说就是0和1相邻时就可以删去同时获得一次电量。由于输入只会出现0和1这两种数字,我们很容易发现,在删除一定数量数字后的某一时刻只要还同时存在0和1,就一定可以继续删除(正所谓水火不相容),直到字串中只含有0或者只含有1。所以只要计算出0和1各自出现的数目,较小的那个数就是答案

难度:1颗星

#include <stdio.h>

char ch[100005];

int main()
{
    int zero = 0, one = 0;//记录0和1的数目
    scanf("%s", ch);
    for (int i = 0; ch[i] != '\0'; i++)
    {
        if (ch[i] == '0')
            zero++;
        else
            one++;
    }
    //输出较小值
    if (zero > one)
        printf("%d", one);
    else
        printf("%d", zero);
    return 0;
}

C.我的书馆

解题思路:

 这是一个模拟题,首先需要对题目有一定的了解。

题意:拿书的顺序是绝对一定的(按照年份顺序),也就是我们每次去哪一层是确定的,只要按照年份排个序,对应的楼层顺序就是我们要走的顺序。

解题关键:因为只要下楼就一定要一直下到一楼,我们可以简单的画出这样一个图代表我们的轨迹(假设我们需要先后前往3,2,4,5,3,4楼拿书,横坐标表示时间,纵坐标表示楼层)

是不是注意到了什么?这里有几个像山一样的东西。

没错,我们每次上楼时一定会一直上到极大值点,下楼时也一定会下到最小楼层,所以我们可以只考虑极大值点即可

这样一来,我们需要走的楼层数目其实就是所有极大值点的两倍,问题也就转化成了求所有极大值点。  上面这个样例的答案也就是(3+5+4)*2=24。

难度:3颗星

#include <bits/stdc++.h>
using namespace std;

int n, m;
struct floor //存储楼层与对应时间
{
    int level;
    int time;
    bool operator<(const struct floor &t) const //按时间排序
    {
        return time < t.time;
    }
} a[105];

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        cin >> a[i].level >> a[i].time;
    sort(a, a + n);//排序

    int res = 0;
    int flag = 0;//flag记录当前状态,1表示下降,0表示上升
    for (int i = 1; i <= n; i++)
    {
        if (flag == 0 && a[i].level < a[i - 1].level)
        {
            res += 2 * (a[i - 1].level - 1);
            flag = 1;
        }
        else if (flag == 1 && a[i].level > a[i - 1].level)
            flag = 0;
    }
    cout << res << endl;
    return 0;
}

D.吃席

解题思路:

题意:在一个长度为n的环形区域内输出最大长度为m的区间和。如图:

思路:维护一个长度为m的区间和,将区间每次往右边移动一位,每移动一位之后区间和加上新增的数并减去减少的数。

难度:2颗星

#include <stdio.h>

int vi[100005];

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++)
        scanf("%d", &vi[i]);
    int ans = 0, res;     //res记录最大区间和
    //可以将ans的值初始化为第一个区间和
    for (int i = 0; i < m; i++)
        ans += vi[i];
    res = ans;
    //循环遍历每一个窗口,i表示左边界,j表示右边界
    for (int i = 1; i < n; i++)
    {
        int j = (i + m - 1) % n;  //记得取模
        ans += vi[j] - vi[i - 1];
        if (ans > res)
            res = ans;
    }
    printf("%d", res);
    return 0;
}

E.嘉嘉的序列游戏

解题思路:

首先需要对序列进行一次排序(顺序不影响结果),这样可以保证我们从前往后遍历的时候前面的数字总是包含在后面的数字中。

我们需要得出一个结论,S[i](i从0开始)转化成A[i]可以有S[i]-i种

解释:首先我们知道一个数 s[i] 一定可以转化成s[i]个小于等于它的数,而当我们从前往后遍历时,我们一定已经使用过i个小于等于S[i]的数了,使用过的数不能重复使用,所以只有S[i]-i个。可能不好理解,建议多思考一下。

难度:4颗星

#include <bits/stdc++.h>
using namespace std;
const int p = 1e9 + 7;

long long a[5005];
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++)
            cin >> a[i];
        sort(a, a + n);     //先排序
        long long res = 1;
        for (int i = 0; i < n; i++)
            if (a[i] - i > 0)
                res = (a[i] - i) * res % p;  //把每个乘起来
            else
                res = 0;   //只要其中有一个数没有选择,那么直接归零
        cout << res << endl;
    }
    return 0;
}

F.情书

解题思路:

解密的关键就在于,原字符串的第一个二进制码一定是1(因为第一个字符是大写字母,大写字母的ASCII码值为65-90),确定了第一个二进制码后,可以保证后面的每个二进制码一定是唯一的。图解:

 字符x的第一位一定是1,所以y的第二位一定是1,又字符z是已知的,所以可以通过异或求出x的第二位,这样y的第三位也就出来了,以此类推可以推出整个x字符串。

难度:4颗星

#include <stdio.h>

char a[2005], b[2005], c[2005];

int main()
{
    while (scanf("%s", a) != EOF)
    {
        int idx = 0, tmp;
        for (int i = 0; a[i] != '\0'; i++)
        {
            if (a[i] <= '9' && a[i] >= '0')
            {
                tmp = 0;
                for (int k = 0; k < 3; k++)
                    tmp = tmp * 10 + a[i + k] - '0';
                i += 2;
                b[idx++] = tmp;
            }
            else
                b[idx++] = a[i];
        }

        int flag = 1;
        c[0] += flag << 6;
        for (int j = 5; j >= 0; j--)
        {
            flag ^= b[0] >> j & 1;
            c[0] += flag << j;
        }
        for (int i = 1; i < idx; i++)
        {
            for (int j = 6; j >= 0; j--)
            {
                flag ^= b[i] >> j & 1;
                c[i] += flag << j;
            }
        }
        for (int i = 0; i < idx; i++)
        {
            printf("%c", c[i]);
            c[i] = 0;
        }
        puts("");
    }
    return 0;
}

G.云顶之奕

模拟题,因为这个题范围比较小,只有4*7,直接暴力枚举所有特殊情况都是可以求出来的,所以难度只有3颗星。

这里解释一种不用考虑特殊情况的方法,代码更加简洁

解题思路:

这是原题目的图:

原图不好用数组进行记录,所以我们需要做一个小小的转化,转化后的矩阵如图

蓝色位置为棋盘,其余位置为空。

这样一来,相邻的点就有了规律,比较容易求出来。

我们需要进行的操作就是每当遍历到的点为1时,将相邻的6个位置加25即可。

结合下面代码理解

难度:3颗星

#include <stdio.h>

int a[5][20], b[5][20];
int dx[6] = {-1, 0, 1, 1, 0, -1}, dy[6] = {1, 2, 1, -1, -2, -1};  //相邻的六个方向

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        for (int i = 0; i < 4; i++)
            for (int j = i % 2; j < 14; j += 2)   //注意这里j的初始值,比较关键
            {
                scanf("%d", &a[i][j]);
                if (a[i][j] == 1)     //如果为1,将相邻点加一次属性
                    for (int k = 0; k < 6; k++)
                    {
                        int x = i + dx[k], y = j + dy[k];
                        if (x >= 0 && x < 8 && y < 14 && y >= 0) //判断边界,防止数组越界
                            b[x][y]++;
                    }
            }
        //输出答案
        for (int i = 0; i < 4; i++)
        {
            for (int j = i % 2; j < 14; j += 2)
            {
                if (a[i][j] != 0)
                    printf("%d ", b[i][j] * 25 + 100);
                else
                    printf("0 ");
                b[i][j] = 0;
            }
            puts("");
        }
    }
    return 0;
}

H.夏季大三角

解题思路:

我们从题目可以得到的信息有:

1) 三角基可能会有四种不同的方向;

2) 一个三角基可以与方向上的多个点构成多个夏季大三角;

解决步骤:

1)模拟三角基的四种方向,找出所有三角基;

2) 用前缀和求出每个方向上各种星星的个数(当然也可以不用前缀和,这个题目能够在线处理,但是用前缀和代码容易写),其中与三角基1/2的星星构成大三角。

难度:5颗星

#include <stdio.h>

int a[2005][2005], sum[4][2005][2005][5]; //a为原数组,sum记录前缀和
int dx[4] = {1, 1, -1, -1}, dy[4] = {1, -1, -1, 1}; //模拟四种方向
char ch[2005];
int n, m;

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%s", ch);
        for (int j = 1; j <= m; j++)
            a[i][j] = ch[j - 1] - '0';  //做个转化方便后续处理
    }
    //预处理数组,求前缀和
    for (int f = 0; f < 4; f++)
        for (int i = dx[f] == 1 ? n : 1; i >= 1 && i <= n; i -= dx[f])
            for (int j = dy[f] == 1 ? m : 1; j >= 1 && j <= m; j -= dy[f])
                for (int k = 0; k <= 4; k++)  
                {
                    sum[f][i][j][k] = sum[f][i + dx[f]][j + dy[f]][k];
                    if (k == a[i][j])
                        sum[f][i][j][k]++;
                }
    //找三角基,相加得结果
    long long res = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            for (int k = 0; k < 4; k++)
                if (a[i][j] == a[i + dx[k]][j] && a[i][j] == a[i][j + dy[k]] && a[i][j] != 9 &&
                    a[i + dx[k]][j + dy[k]] == 9) //注意一定要加a[i][j]!=9,本人刚开始这里没加wa了
                    res += sum[k][i + 2 * dx[k]][j + 2 * dy[k]][a[i][j] / 2];
    printf("%lld", res);
    return 0;
}

I.无限梦境的概率理论

这个题主要考察数学能力,这里提供两种求期望的方法。

难度:3颗星

方法一:

如果计算第i步出货的概率g[i],那么期望就应该等于所有i*g[i]的和,直接上代码:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    double a, b, res = 0, g = 1;//g表示能抽到第i步的概率,初始为1
    int n, i;
    scanf("%lf%d%lf", &a, &n, &b);
    for (i = 1; a < 1; i++)
    {
        res += g * a * i;   //求和
        g *= 1 - a;         //改变g的值
        if (i >= n)
            a += b;
    }
    res += g * i;     //容错提醒,不要忘记最后加上剩余概率
    printf("%.6lf", res);
    return 0;
}

方法二:

求每一步未出货的概率和,该值等价于期望,可以用下图来理解:

0步未出货概率为1,1步时有p1概率出货,这p1的部分就不再参与后面的计算,另外1-p1的部分进入第2步,以此类推。。。。。。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    double a, b, res = 1, g = 1;
    int n;
    scanf("%lf%d%lf", &a, &n, &b);

    for (int i = 1; a < 1; i++)
    {
        g *= 1 - a;
        res += g;
        if (i >= n)
            a += b;
    }
    printf("%.6lf", res);
    return 0;
}

 第二种方法相比第一种少了几次乘法运算,精度更高。不过题目的精度要求是比较低的,两种均可。

理解该题需要一定的数学基础,建议多结合代码理解

J.交叉坐标的星尘

压轴题,想看题解的话可以去看看z3475这篇文章2022新生赛题目&std数据&题解(鳖在这里发电,交叉坐标的星尘,无限梦境的概率理论,夏季大三角)

难度:6颗星

结语

肝了很久,制作不易,希望能对大家有所帮助!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值