每日一题——奖学金

 哈喽大家好,我是保护小周ღ,本期为大家带来的是博主在牛客网上遇到的一道笔试题,题目不难,但是有点复杂(博主太菜了),博主在做题的过程中使用到了,结构体动态内存开辟数据排序,相关知识,包括写着写着被迫使用goto语句,还是值得学习一下的,有没有朋友有其他的解法,欢迎留言啊~

目录

博主题目分析:

程序设计:

 程序实现:

goto语句


描述

小v今年有n门课,每门都有考试,为了拿到奖学金,小v必须让自己的平均成绩至少为avg。每门课由平时成绩和考试成绩组成,满分为r。在考试前,小v他已经知道每门课的平时成绩为ai ,假设付出的时间与获得的分数成正比,若想让这门课的考试成绩多拿一分的话,小v要花bi 的时间复习,不复习的话当然就是0分。同时我们显然可以发现复习得再多也不会拿到超过满分的分数。问小v为了拿到奖学金,至少要花多少时间复习?

输入描述:

第一行三个整数n,r,avg(1 <= n <= 105,1 <= r <= 109,1 <= avg <= 106),接下来n行,每行两个整数ai和bi,(0 <= ai <= 106,1 <= bi <= 106) 注意:本题含有多组案例输入。

输出描述:

每个用例对应一行输出答案。

示例1

输入:

5 10 9
0 5
9 1
8 1
0 1
9 100
3 5 3
2 1
4 100
3 3

输出:

43
0

说明:

示例1有两组测试用例。 对于第2组测试用例,小v的平时成绩的平均成绩为(2+4+3)/3=3分,已经达到拿奖学金的最低要求,所以可以不用复习。


博主题目分析:

  1. 首先,小V想要拿奖学金,所以他必须保证,他的各科总平时成绩大于等于 avg ,每一门课的满分为 r ,然后 n 代表的是小V的所有学科。
  2. 第一轮输入 n=有多少门课,r=科目的满分是多少, avg=总平均成绩要达到avg,才能获得奖学金。
  3. 第二轮多组输入,输入 ai =该门课的平时成绩, 输入 bi =如果想要这门课的成绩提升一分,需要花多少时间复习。然后输入下一门课的ai 和bi;
  4. 复习得再多也不会拿到超过满分的分数。

程序设计:

题目要求使用多组输入,所以我们利用while()循环和scanf()函数的返回值,实现多组输入。

第一次输入,n , r ,avg ;

然后我们是不是应该定义一个数组,来记录小V同学的每门课的平时成绩,和如果想要这这些课的成绩提升一分,需要花多少时间复习。这两个种数据。

定义什么?结构体类型的数组,因为同一门课的ai 和 bi 是有联系的,用结构体来描述,再适合不过。

typedef struct student
{
    int ai;//每门课的平时成绩
    int bi;//该门功课如果要涨分要花的时间
}student;

博主这个名字取得不太好,但是使用还是没有问题滴。

小V有几门科目,咱们就开辟多大的空间,存储每一门的平均分数,和对应复习涨分所需要的时间

student* xiaoV = (student*)malloc(sizeof(student) * n);
if (xiaoV == NULL)//如果开辟失败就没必要继续了
{
   perror("malloc");//报个错
   exit(-1);
}

第二轮循环输入 每门科目的 ai 和 bi ,此时呢,我们还应该记录一下这些科目的平时成绩之和。

假如: 我们光靠平时成绩的平均分就>=avg,拿到奖学金,就不需要复习了。打印0即可。

int sum = 0;//统计小V同学各科的平时成绩和
for (int i = 0; i < n; ++i)
{
  scanf("%d %d", &xiaoV[i].ai, &xiaoV[i].bi);
  sum += xiaoV[i].ai;
}
//如果小V同学的各科平时成绩就可以达到拿奖学金的程度。那就不用复习了
if (sum / n >= avg)
printf("0\n");

否则:那就要复习了,题目要求:至少要花多少时间复习?

所以,我们复习啊,必须要从涨分容易的科目开始,花的时间少,还能涨分,那我们优先要先找到那些涨分代价小的复习,然后记录一下复习花的时间,每涨一分,咱们就判断一下是否达到拿奖学金的程度,如果可以拿到奖学金,咱们就中止复习操作,然后输出花的时间即可。如果没有拿到,咱们就继续复习,涨分,判断,然后等该门课复习至满分后,继续复习下一门涨分代价小的复习重复操作。

这个点主要的是找复习代价小的,通过什么判断呢,xiaoV[i].bi,小V需要花多少时间复习该门课涨一分。这个值越小越好啊,博主也没啥别的本事,排序耍的还行,我们直接将xiaoV的所以有科目按照xiaoV[i].bi,让xiaoV[i];从小到大排序,然后遍历,执行我上面说的操作。

大家应该能够理解吧?理解的同学扣波1 ,咱们互动一下。


 程序实现:

#include<stdio.h>
#include<stdlib.h>
typedef struct student
{
    int ai;//每门课的平时成绩
    int bi;//该门功课如果要涨分要花的时间
}student;

int main()
{
    int n=0;//多少门功课
    int r=0;//满分
    int avg=0;//拿奖学金的平均分
    while(scanf("%d %d %d",&n,&r,&avg)!=EOF)
    {
        //小V有几门科目,咱们就开辟多大的空间,存储每一门的平均分数,和对应复习涨分所需要的时间
        student *xiaoV=(student*)malloc(sizeof(student)*n);
        if(xiaoV==NULL)//如果开辟失败就没必要继续了
        {
            perror("malloc");//报个错
            exit(-1);
        }
        
        int sum=0;//统计小V同学各科的平时成绩和
        for(int i=0;i<n;++i)
        {
            scanf("%d %d",&xiaoV[i].ai,&xiaoV[i].bi);
            sum+=xiaoV[i].ai;
        }
        //如果小V同学的各科平时成绩就可以达到拿奖学金的程度。那就不用复习了
       if(sum/n>=avg)
           printf("0\n");
        else
        {
            //冒泡排序,使涨分花时间代价小的排在前面,优先处理
            //当然也可以使用qsort()库函数排序
            for(int i=0;i<n;++i)
            {
                for(int j=0;j<n-i-1;++j)
                {
                    if(xiaoV[j].bi>xiaoV[j+1].bi)//小的放在前面
                    {
                        student tmp=xiaoV[j];//定义结构体student类型的变量临时存储、交换
                        xiaoV[j]=xiaoV[j+1];
                        xiaoV[j+1]=tmp;
                    }
                }
            }
            long time=0;//记录复习提分所需要的时间、因为输入案例较大所以使用long 型
            for(int i=0;i<n;++i)//判断某一门科目
            {
                //不会拿到超过满分的分数作为判断该科目能涨多少分的结束条件
                for(int j=xiaoV[i].ai+1; j<=r ;++j)//判断该门科目能涨多少分,并且记录所花的时间代价
                { 
                    time+=xiaoV[i].bi;//统计该科涨分的时间代价
                    ++sum;//花时间复习就涨一分
                    if(sum/n>=avg)//每涨一分我们就看看是否达到拿奖学金的最低分数
                    {
                        printf("%ld\n",time); 
                        free(xiaoV);//释放掉我们动态开辟的空间
                        goto end;//我们使用goto跳转出循环
                    }
                }
            }
        }
     end: ; //跳转至多组while循环的末尾,使其可以继续多组输入
    }
    return 0;
}

goto语句

这里博主使用了goto 语句,关于goto 语句,博主简单讲两下:

C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。 从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。 但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过 程。 例如:一次跳出两层或多层循环。 多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。就像博主遇到的情况,已经达到可以拿到奖学金的程度,就没必要复习了。如果break;中止内部循环,实际上,外循环还是会继续运行的,没必要了。

goto语言真正适合的场景如下: 

for(...)  { 

      for(...)   {      

             for(...)   {            

                              if(disaster)  

                              goto error;      

             }  

      }    …

}

error: ;

 


感兴趣的朋友可以用博主的方法,或者是自己的方法做做这道题,优化一下代码。

 题目来源于牛客网

题目链接:奖学金_牛客题霸_牛客网 (nowcoder.com)

本期收录于博主的专栏——每日一题,适用于编程初学者,有兴趣的朋友们可以订阅,查看其它“精彩小题”。每日一题_保护小周ღ的博客-CSDN博客

感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ  *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

​​

  如有侵权请联系修改删除!

  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

保护小周ღ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值