2024.3.1|牛客练习赛122
心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的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}
(i∑c[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;
}
心有猛虎,细嗅蔷薇。再见了朋友~