【Code pratice】——星期一、猜年龄、合并检测、生日蜡烛

发布于:2022-11-29 ⋅ 阅读:(142) ⋅ 点赞:(0)

D a t e : 2022 − 10 − 03 \color{FF9999}{Date:2022-10-03} Date20221003

T h a n k s \color{FF9999}{Thanks} Thanks t h e \color{FF9999}{the} the s u n \color{FF9999}{sun} sun r i s e s \color{FF9999}{rises} rises a g i a n , \color{FF9999}{agian,} agian, c o n t i n u e \color{FF9999}{continue} continue t o \color{FF9999}{to} to l i g h t \color{FF9999}{light} light m y \color{FF9999}{my} my d r e a m ! \color{FF9999}{dream!} dream!

🥝1. 星期一🥝

🥥题目🥥

整个20世纪(1901年1月1日至2000年12月31日之间),一共有多少个星期一?

🥥思路🥥

  1. 先确定1901年1月1日为星期几
  2. 以第一个星期一的当天开始计算
  3. 每过7天则有一个新的星期一
  4. 重复2~3步骤直到2000年12月31日为止

🥥代码🥥

int Monday(int i_bYear, int i_bMon, int i_bDay, int i_eYear, int i_eMon, int i_eDay, int i_initWeekDay)
{
    if ((1900 > i_bYear) || (1900 > i_eYear)
     || (!IsValidDate(i_bYear, i_bMon, i_bDay) || !IsValidDate(i_eYear, i_eMon, i_eDay))
     || ((1 > i_initWeekDay) || (7 < i_initWeekDay)))
    {
        cout << "Invalid date entered." << endl;
        return -1;
    }
    
    int averageDay = 0;     // 平年总天数
    int leepDay = 0;        // 闰年总天数
    int allDay = 0;         // 全年总天数
    int firstMonDay = 0;    // 起始日期距第一个星期一的天数
    int result = 0;         // 星期一总天数

    for (int i = i_bYear; i <= i_eYear; i++)
    {
        if (Pikashu_IsLeapYear(i))
        {
            leepDay += 366;
        }
        else
        {
            averageDay += 365;
        }
    }
    allDay = leepDay + averageDay;
    firstMonDay = 8 - i_initWeekDay;

    for (int curWeekDay = i_initWeekDay; curWeekDay <= allDay - firstMonDay; curWeekDay += 7)
    {
        result++;
    }
    return result;
}

🍈2. 猜年龄🍈

🍐题目🍐

一个人的年龄的立方是个四位数,四次方是个六位数,这10个数字正好包含0到9这10个数字,每个都恰好出现一次,请判断输入的值是否是这个年龄值

🍐解法🍐

思路:本题的关键点在于这个年龄的立方值和四次方值 —— 正好包含 0 ~ 9 这10个数字,切都只出现一次。那么只需要记录 0 ~ 9 这10个数字出现的次数就能得出年龄值了。

🍐代码🍐

if (input不规范)
{
    return -1;
}
int array[10] = {0};       // 记录 0 ~ 9 出现的次数
while (立方(四次方))
{
    tmp = 个位数;
    array[tmp]++;
    立方(四次方) /= 10;
}
for ()
{
    if (array[i] != 1)
    {
        // 不符合
    }
}

总结
刚开始写的时候,因为习惯性的做了函数接口的入口保护,所以把立方和四次方的长度也考虑了进来,但是写完代码验证之后发现,入口保护是必要,但是在本题中立方正好是四位数,四次方正好是六位数,这个点其实不必过多关注,因为无论其立方值和四次方值是多少位数,只要最后 0 ~ 9 这10个数字中存在出现次数为0,或者大于1的那就一定不符合要求。(当然,这个长度的校验,好像也可以挡住很多不正确的值,时间空间复杂度的计算还不太明确,所以也不知道这个长度的校验是浪费时间空间还是节省时间空间)

🥑3. 合并检测🥑

🥒题目🥒

将 K 个人的采集标本放到同一个试剂盒中进行检测。如果结果为阴性,则说明这 K 个人都是阴性,用一个试剂盒完成了 K 个人的检测;如果结果为阳性,则说明至少一个人为阳性,需要将这 K 个人的样本全部重新独立检测,加上最开始使用的合并检测,一共使用了 K + 1 个试剂盒完成了 K 个人的检测(民众感染率大概为1%,呈均匀分布),请问 K 取多少能最节省成本?

🥒解法(暴力计算 + 数学公式)🥒

思路:题目所求的是最节省成本的K值,关键在于成本是什么?本题中的成本就是所要使用的试剂盒总数,试剂盒总数越少,成本越低,换言之就是求当检测人数固定时,K为何值,消耗试剂盒的数量最少?根据题目所说,试剂盒总数的计算方式:总数 = 合并检测消耗试剂盒数 + 阳性所在组消耗试剂盒数。为了方便计算,且因为民众的感染率为1%,且均匀分布,所以假设本次检测人数为100人,那么计算方式的式子即为:sum = 100 / k + k,所以本题就简化为求式子中sum为最小值时的K值

🥕暴力计算🥕

通过遍历计算当K = [1, 100]时,所消耗的试剂盒总数,取这100个总数最小值对应的K值为答案

人数 = 100;
合并检测消耗试剂盒数 = 100 / K;
阳性病例所在组消耗试剂盒数 = K;
消耗试剂盒总数 = 100 / K + K;
int array[100] = {0};   // 存放试剂盒总数值
for (k)
{
    array[k - 1] = 100 / k + k;
}
for (int i = 0; i < 100; i++)
{
    if (min = array[k])
    {
        return (i + 1);
    }
}

🥕数学公式🥕

根据数学知识可知,sum = 100 / k + k 为对勾函数(对勾函数的定义为:y = a*x + b/x),这类型的函数最小值为:2 b \sqrt b b a \sqrt a a ,最小值对应的x值为: b \sqrt b b / a \sqrt a a ,由此就可以很方便的计算出K值了
至此就剩一个点需要解决,那就是怎么计算根号值,这可以用<cmath>提供的库函数pow()进行计算,当然也可以自己写一个函数进行计算

人数 = 100;
合并检测消耗试剂盒数 = 100 / K;
阳性病例所在组消耗试剂盒数 = K;
消耗试剂盒总数 = 100 / K + K;
result = (100^0.5) / 1^0.5;
return result;

代码

int MergeDetect(int i_Sel)
{
    int result = -1;
    if ((1 != i_Sel) && (2 != i_Sel))
    {
        return result;
    }
    switch (i_Sel)
    {
    case 1:
        {
            vector<int> ReTestkitSum(100, 0);
            for (int i = 1; i <= 100; i++)
            {
                if (0 == 100 % i)
                {
                    ReTestkitSum[i - 1] = 100 / i + i;
                }
                else
                {
                    ReTestkitSum[i - 1] = 100 / i + i + 1;
                }
            }
            int min = ReTestkitSum[0];
            for (int i = 0; i < ReTestkitSum.size(); i++)
            {
                if (min > ReTestkitSum[i])
                {
                    min = ReTestkitSum[i];
                    result = i + 1;
                }
            }
        }
        break;
    case 2:
        {
            result = pow(100, 0.5) / pow(1, 0.5);
        }
        break;
    
    default:
        break;
    }
    
    return result;
}

🌴4. 生日蜡烛🌴

🌵题目🌵

某君从某年开始每年过生日都要吹熄年龄数的蜡烛,现在他已经吹灭了236根蜡烛了,求其从哪岁开始过生日吹蜡烛

🌵思路🌵

假设从 X 岁开始过生日,且每年都要吹熄与年龄数相同根数的蜡烛,那么 X 的范围就是[1, 118)(为什么最大年龄开区间是118,因为如果 X = 118,那么第二年吹熄119根,总和就为 237 > 236),那么可以直接暴力计算 [1, 118) 这个范围内的值为开始岁数,进行逐年累加,如果总和等于 236,则得出 X 的值,如果某年开始总和大于236,那这个值一定不符合,跳出循环进行下一个值的检测

for (X)
{
    for (i = X)
    {
        sum += i;
        if (236 == sum)
        {
            return i;
        }
        if (236 < sum)
        {
            break;
        }
    }
}

🌵代码🌵

int BirthdayCandles()
{
    int maxAge = 118;
    for (int i = 1; i < maxAge; i++)
    {
        int ageSum = 0;
        for (int j = i; j < maxAge; j++)
        {
            ageSum += j;
            if (236 == ageSum)
            {
                return i;
            }
            if (236 < ageSum)
            {
                break;
            }
        }
    }
    return -1;
}

在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看