2024.3.2|AcWing第145场周赛

2024.3.1|牛客练习赛122

A.平均成绩
B.套餐设计
C.分班

心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。
在这里插入图片描述

平均成绩

题目

贝茜期末考了三门课,成绩分别为 a,b,c。

请你计算,三门课的平均成绩是否超过了 60分。
输入样例1:
60 70 80
输出样例1:
YES
输入样例2:
50 50 70
输出样例2:
NO

注意
A题是签到题,求平均值但是尽量避免用除法(涉及向下/向上取整和浮点数)。

实践代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

void solve(){
    int a,b,c;cin>>a>>b>>c;
    int sum = a+b+c;
    if(sum>180) cout<<"YES"<<endl;
    else cout<<"NO";
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

套餐设计

题目
给定 m个食物,其中第 i 个食物的种类为 ai。

请你设计一个食物套餐,对于该套餐:

唯一要求是设计好的套餐必须恰好包含 n个食物。
具体包含多少种食物,以及包含哪些种类的食物,不做要求,任你安排。
每种食物具体包含多少个,不做要求,任你安排。
我们的目标是通过合理安排套餐中包含的食物内容,从而使得利用给定食物,可以制作出的该套餐的数量越多越好。

输出能够制作出的套餐的最大可能数量。

输入描述:
第一行包含两个整数 n,m。

第二行包含 m个整数 a1,a2,…,am
输出描述:
一个整数,表示能够制作出的套餐的最大可能数量。

如果根本不可能制作出任何套餐,则输出 0。
数据范围

前 4 个测试点满足 1≤m≤10。
所有测试点满足 1≤n≤100,1≤m≤100,1≤ai≤100。
输入样例1:
4 10
1 5 2 1 1 1 2 5 7 2
输出样例1:
2
输入样例2:
100 1
1
输出样例2:
0
输入样例3:
2 5
5 4 3 2 1
输出样例3:
1
输入样例4:
3 9
42 42 42 42 42 42 42 42 42
输出样例4:
3

注意
B题可以使用二分找到最大套餐数。
1.我们先设能够做成t份套餐。
2.我们设第i种食物的总量为c[i]且每份套餐中第i种食物的数量为b[i]。
3.但是可知前提是t份套餐中每一份所含食物数量必须大于等于n。(条件1:b[i]*t≤c[i]->b[i] = c[i]/t)
(条件2:b[1]+b[2]+…+b[i]=n)
4.根据条件1和条件2可得 ( ∑ i c [ i ] / t ) ≥ n (1) (\sum_{i} c[i]/t)≥n \tag{1} (ic[i]/t)n(1)

又由于自变量是t且在分母上,可知f(t)= ( ∑ i c [ i ] / t ) (\sum_{i} c[i]/t) (ic[i]/t)是单调递减的函数。所以可以简化题目:找f(t)≥n的最大的一个t

实践代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 110;
int n,m;

map<int,int> mp;
bool check(int mid){//判断当套餐数为mid时每份套餐的食物总数是否达到n
    int sum=0;
    for(int i=1;i<N;i++) sum+=mp[i]/mid;
    return sum>=n;
}
void solve(){
    cin>>n>>m;//每种套餐的食物数和食物总数
    for(int i=0;i<m;i++){
        int x;cin>>x;//每个食物种类
        mp[x]++;//记录x类型食物的个数
    }
    int l=0,r=m/n;//套餐数在[0,m/n]的区间上

    while(l<r){//二分
        int mid=l+r+1>>1;//右移运算符相当于整数除以2并向下取整
        if(check(mid)) l= mid;
        else r=mid-1;
    }
    cout<<r<<endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

分班

题目

某年级一共有 m=n×k个学生,编号 1∼m。
其中,第 i个学生的学习能力为 ai。

学校计划将这些学生平均分配到 n个班级,每个班级恰好包含 k个人。

为了保证全班学生都能跟得上讲课,每个班的讲课速度都等于班级内学习能力最低的学生的学习能力。

学校希望班级之间的学习进度不能相差太多,具体来说,任意两个班级之间的讲课速度的差异都不得超过 l。

在此前提下,学校希望通过合理分班,使得所有班级的讲课速度之和尽可能大。

请你计算并输出所有班级的讲课速度之和的最大可能值。

输入格式
第一行包含三个整数 n,k,l。

第二行包含 m=n×k个整数 a1,a2,…,am。

输出格式
输出一个整数,表示所有班级的讲课速度之和的最大可能值。

如果不存在满足要求的分班方案,则输出 0。

数据范围
前 4个测试点满足 1≤m≤10。
所有测试点满足 1≤n,k,m≤105,0≤l≤109,1≤ai≤109。

输入样例1:
4 2 1
2 2 1 2 3 2 2 3
输出样例1:
7
输入样例2:
2 1 0
10 10
输出样例2:
20
输入样例3:
1 2 1
5 2
输出样例3:
2
输入样例4:
3 2 1
1 2 3 4 5 6
输出样例4:
0

注意
C题是一个简单的贪心算法。我们先对学生按照学习能力排序,可知无解的情况是a[n-1]>a[0]+l,不满足每个班讲课速度差异都小于l。而剩下的就是求最优解的情况,我们仔细一想,题目让求讲课速度之和最大值(且讲课速度与班里学习能力最低的那个人有关),那么把相对最好的放在一个班,接下来次之,最后的是最差的班,可以先把学生(已按学习能力排序)分成left(0 - a[0]-l)和right两部分,第一个班我们就可以先在left里取1个最大值然后再在right中取k-1个学生,下一个班次之,但是要考虑一个边界情况,就是k-1>right,我们取完right之后还要再在left里取k-right个值

实践代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'

void solve(){
    int n,k,l;
    cin>>n>>k>>l;
    int m = n*k;
    vector<int> a(m);
    for(int i=0;i<m;i++) cin>>a[i];
    sort(a.begin(),a.end());
    if(a[n-1]>a[0]+l) cout<<"0";//判断无解的情况
    else{
        //最优解:将同学(已按学习能力排序)分成两部分:每个班从left(取1个)和right(取k-1个)
        int i = n-1;
        while(i+1<m&&a[i+1]<=a[0]+l) i++;//找到left的右边界(即a[0]+l)定义为i
        int right = m-1-i;//right = 总人数 - left
        int res=0;
        for(int j=0;j<n;j++){//分n个班
            //这里从right中取的时候一定要考虑边界情况,即取的k-1>right,则要再从left中取
            int can = min(k-1,right);//从右边拿k-1个人
            right -= can;//右边剩下的人
            int left = k - can;//从left中再取(k-(k-1))=1个/(k-right)个
            i -= left;//下一次从i-left的地方开始选
            res += a[i+1];//一个班的最小值
        }
        cout<<res<<endl;
    }
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

心有猛虎,细嗅蔷薇。再见了朋友~

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值