郑州大学“战疫杯”大学生程序设计在线邀请赛(6)(C++题解+详细思路)2022年5月14日

目录

1 寝室分西瓜

2 隔离警戒线

3 无接触水龙头

4 掷骰子


1 寝室分西瓜

小W排了很久的队买了好几个西瓜,西瓜的重量是整数斤的,他和室友们分着吃。非常喜欢偶数的他们决定把这些西瓜分别分成偶数斤的六份,现在考考你能不能按这种方式分这些西瓜?如果能分的话,最重的那一份比最轻的那一份最少多多少斤?

输入格式:

第一行一个正整数n表示西瓜的数量。
接下来n行每行一个正整数ai​表示当前西瓜的重量。

输出格式:

输出n行。
如果第i个西瓜可以分成六份偶数斤就输出一个整数x表示分的最重的那一份比最轻的那一份最少多多少斤。

如果第i个西瓜不能这样分则输出-1。

输入样例:

在这里给出一组输入。例如:

2
14
15

输出样例:

在这里给出相应的输出。例如:

2
-1

样例解释1

第一个西瓜只有一个分法:分成2,2,2,2,2,4,最重的比最轻的多2斤。

第二个西瓜不能分成偶数斤的六份。

数据范围与提示

1≤n≤10

1≤ai​≤100

思路:

若 n 为奇数(没法分偶数),或 n 小于 12(不够分偶数),输出 - 1

若 n 可以分,若 n 可以整除 12 ( n 可以每人分一样的一份),输出 0,若无法整除 12,则余数必为 2,因为题目意思为 最大值 与 最小值的 最小差值。

举例:16 % 12 = 4,可以分为 2 2 2 2 4 4,22 % 12 = 10,可以分为 2 4 4 4 4 4,总之只要不是-1的情况的无法整除12的数,一律输出 2

代码如下:

#include <bits/stdc++.h>

using namespace std;

typedef long long int LL;

const int N = 2e5+10;

int n, m;

int main()
{
    int T;
    scanf("%d", &T);

    while(T -- )
    {
        int n;
        scanf("%d", &n);
        if( n%2 || n < 12)
        {
            printf("-1\n");
            continue;
        }
        else
        {
            int res = 0;
            if(n % 12 != 0) res = 2;
            printf("%d\n", res);
        }
    }

    return 0;
}

2 隔离警戒线

郑州封控了多个社区,现在把这些社区抽象成二维平面上的互不重叠的长方形,问你给这些社区拉警戒线需要多少单位长度的警戒线?

输入格式:

第一行一个正整数n表示封控社区的数量。
接下来n行每行四个正整数x1,y1,x2,y2(1≤x1<x2≤1e6,1≤y1<y2≤1e6)表示被封控社区的左下角坐标和右上角坐标。

输出格式:

输出一行一个正整数表示需要警戒线的长度。

输入样例:

3
2 1 4 3
4 1 6 3
3 3 5 5 

输出样例:

20

样例解释

样例的三个封控地区如上所示,需要20单位长度的警戒线。

数据范围与提示

1≤n≤100

1≤x1<x2≤1e6,1≤y1<y2≤1e6

思路:

        (1) 首先明确变量含义,x1y1为矩形左下点,x2y2为矩形右上点

        (2) 计算总长度等于,每个小方框的周长 - 重合的边的长度,输入一个方框枚举一下与之前的方块是否重合,重合则减去重合边长

        (3) 如何计算重合的长度?

两个方框有两种状态重合,举例

如图所示可化为重合的两种方式

(此题不能变化,此例子帮助理解两种情况而已) 

当 y 轴重合,即如上图所示的情况,第一种重合为 x1[ j ] == x2[ i ] ,第二种重合为 x1[ i ] == x2[ j ] ,计算重合的部分(红色部分)的长度,可如此计算(和 WAWA鱼大佬学习的方法),

两上边取最小值 - 两下边取最大值 ,再与零 取最值,代码解释为:

max(0, min(y2[i], y2[j]) - max(Y1[i], Y1[j]));

若两边无交集,则第二项为负数,如此则减去0,反之为正数,则减去重复的部分

对于 x 轴的重合部分亦是如此

max(0, min(x2[i], x2[j]) - max(x1[i], x1[j]));

详细代码如下:

#include <bits/stdc++.h>

using namespace std;

typedef long long int LL;

const int N = 110;

int n, m;
int x1[N], Y1[N], x2[N], y2[N];

int main()
{
    scanf("%d", &n);

    int res = 0;
    for(int i = 0; i < n; i ++ )
    {
        scanf("%d%d%d%d", &x1[i], &Y1[i], &x2[i], &y2[i]);
        res += (x2[i] - x1[i]) * 2 + (y2[i] - Y1[i]) * 2;
    }

    for(int i = 0; i < n; i ++ )
    {
        for(int j = 0; j < i; j ++ )
        {
            if(x1[i] == x2[j] || x2[i] == x1[j])
                res -= max(0, min(y2[i], y2[j]) - max(Y1[i], Y1[j]));
            else if(Y1[i] == y2[j] || y2[i] == Y1[j])
                res -= max(0, min(x2[i], x2[j]) - max(x1[i], x1[j]));
        }
    }

    cout << res << endl;

    return 0;
}

3 无接触水龙头

新冠疫情席卷全球已经两年有余,每个人都在为战胜这场灾难贡献自己的力量,希望这场灾难早日过去。

为了减少公共场所人与人间接接触的概率,大学生小D发明了一款自动洗手器。这款机器基于红外线感应的原理,用户只需要把手伸到水龙头下方即可自动出水,不需要再动手操作开关,这样就减少了病毒残留在开关处传播的风险。

小D这款设备的运行逻辑是:当设备的出水倒计时不为0时,水龙头会出水,并且倒计时随着时间减少。每当红外线感应器感应到有人伸手,就会将出水倒计时重置为t秒,出水倒计时为0秒时停止出水。

现在小D给了你感应器的记录列表,里面记录了在什么时间感应到了用户伸手,想请你计算在设备的运行过程中,总共流了多少秒的水。这样以便他对设备继续更新。

输入格式:

第一行两个整数n,t (1≤n≤105,1≤t≤109)分别表示记录的条数和仪器每次出水的时间。
其后n行,每行是一个时间,表示在该时刻感应到了用户使用该仪器。
时间的格式是 yyyy-MM-dd HH:mm:ss。其中y是年份,M是月份,d是日期,H是小时,m是分钟,s是秒数。 比如: 2022-05-08 01:12:34,表示:2022年5月8号1点12分34秒。
数据保证年份是2020年-2022年。
不保证时间的顺序。

输出格式:

输出一行一个整数,表示水龙头在运行过程中总的出水秒数。

输入样例:

2 10
2020-01-01 12:00:09
2020-01-01 12:00:00

输出样例:

19

 思路

预处理:

        将输入的时间转化成秒来计算,此处可用二维数组把三年中的每一个月都表示出来(也可以表示平年的月份,再判断闰年),计算从2020-01-01到输入时间的时间差(单位:秒),排序时间差;

计算 res(结果):

        两数的差与水龙头单词流水的时间(ll) 取 min ,最后再加上一个水龙头单次流水的时间,最后一次一定流完,输出结果即可

代码如下:

#include <bits/stdc++.h>

using namespace std;

typedef long long int LL;

const int N = 2e5+10;

int n, m;
int len[3] = {0, 366, 731};
int mon[3][13] = {{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
                  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
                  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};


int main()
{
    LL T, ll;
    scanf("%lld %lld", &T, &ll);

    LL time[N];
    for(int i = 0; i < T; i ++ )
    {
        int year, m, d, hh, mm, ss;
        scanf("%d-%d-%d %d:%d:%d", &year, &m, &d, &hh, &mm, &ss);
        time[i] = len[year-2020] * 24 * 3600;
        for(int j = 1; j < m; j ++ )
            time[i] += mon[year-2020][j]*24*3600;
        time[i] += (d-1) * 24 * 3600;
        time[i] += hh*3600 + mm*60 + ss;
    }

    sort(time, time + T);

    LL res = 0;
    for(int i = 1; i < T; i ++ )
        res += min((time[i] - time[i-1]), ll);

    printf("%lld\n", res + ll);

    return 0;
}

4 掷骰子

511寝室有6名同学,分别是小A,小B,小C,小D,小E和小F。由于每天吃盒饭,宿舍中产生了很多垃圾,所以他们需要每天都有人下去把全宿舍的垃圾都倒掉。

前六天,他们轮流下去倒垃圾(第一天是小A,第二天是小B....第六天是小F),一轮过后,他们感觉这样很无聊,所以从第七天开始,他们决定用掷骰子的方式,选出每天倒垃圾的人。

掷骰子的规则是:

每人每天掷出一个色子

1、如果有一个人点数最小且没有人与他并列,那么他就要去倒垃圾

2、否则,在色子点数并列最小的这些人中,选出幸运值最大的去倒垃圾。

定义x的幸运值为:x上一次倒垃圾的日子距离今天的天数。

(例如,小A在第七天的幸运值为6,因为他上一次去倒垃圾的日子是第1天)

现在给出这n天(从第7天到第n+6天)掷骰子的结果,请计算出这n天倒垃圾的人分别是谁。

输入格式:

第1行 一个整数n(n≤100),表示掷骰子的天数。

下面n行,每行6个数字,第i行为Ai​ Bi​ Ci​ Di​ Ei​ Fi​,分别代表第i+6天每个人分别掷出的点数。保证点数为1~6之间的整数。

输出格式:

n行,每行一个字母。(ABCDEF其中的一个)

其中,第i行输出第i+6天去倒垃圾的人。

输入样例:

在这里给出一组输入。例如:

3
1 5 4 3 6 1
3 6 2 4 3 2
2 1 3 6 4 3

输出样例:

A
C
B

 思路

        开一个 last 数组记录此人上一次的时间,找出本次的最小值,一个则直接算,多个则比较本次与上一次的相差天数,相差天数大的人先上,最后记得更新一下 倒垃圾人的 last 值

代码如下

#include <bits/stdc++.h>

using namespace std;

typedef long long int LL;

const int N = 10;

int n, m;
int last[N] = {0, 1, 2, 3, 4, 5, 6};

int main()
{
    int T;
    scanf("%d", &T);

    for(int tt = 7; tt < 7 + T; tt ++ )
    {
        int mi = 7, cnt = 1, x;
        int a[N];
        vector<int> b;
        
        // 查找最小值的数值
        for(int i = 1; i <= 6; i ++ )
        {
            scanf("%d", &a[i]);
            if(a[i] < mi) mi = a[i];
        }
        
        // 查找最小值的人
        for(int i = 1; i <= 6; i ++ )
            if(mi == a[i]) b.push_back(i);

        // 判断
        int lmi = tt - last[b[0]];
        int t = b[0];
        for(int i = 1; i < b.size(); i ++ )
            if( tt - last[b[i]] > lmi ) lmi = tt - last[b[i]], t = b[i];

        // 更新 last
        last[t] = tt;

        printf("%c\n", 'A' + t - 1);
    }

    return 0;
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AC自动寄

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

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

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

打赏作者

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

抵扣说明:

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

余额充值