2024-第四批C++ 第一轮选拔赛(代码加解析)

文章讲述了四个编程题目,涉及时间计算、班长选举中票数计算、水杯倒水模拟和硬币组合计数,展示了C++代码实现和解题思路。
摘要由CSDN通过智能技术生成

        莆田市第四批C++选拔 第一轮比赛已经结束了,那么如果需要订正的话,可以点赞收藏。如果没有参加的话也可以知识总结,希望对你有帮助。

        以下代码和解析都是本人写的,都是100分的,只是非官方答案。

一、光阴似箭

题目背景

        光阴似箭,日月如梭。时钟滴滴答答,时间一分一秒的过去,我们总是在不知不觉中度过了一天又一天。

题目描述

        因为地球自转的原因,我们人类实行二十四小时制,将一天分为了 24 小时,每小时 60 分钟。现已知当前时间是 a 小时 b 分钟,请问经过 s 分钟后的时间是多少?

        请注意,每天的时间是循环的,即 23 时 59 分后的 1 分钟后是 0 时 0 分。

输入格式

        一行三个整数 a,b,s,分别表示当前时间的小时、分钟和经过的时间。

输出格式

        一行两个整数,分别表示 s 分钟后的时间的小时和分钟。

样例数据

输入样例 #1
3 5 10
输出样例 #1
3 15
输入样例 #2
23 59 1
输出样例 #2
0 0

数据范围

对于 50% 的数据,b+s\leq 59

对于 100% 的数据,0\leq a\leq 230\leq b\leq 590\leq s\leq 10^{^{9}}

解析

        首先,我们看到这道题,必须先想一想它让我们求的是时间,那么我们可以将 s 除以1440,因为一天刚好有1440分钟,加一天也是原来的时间。比如说:s为2880,a为13,b为30,那么过完2880分钟,也是原来的时间。就算加几天都是原来的时间,不仅这样可以优化代码,还让后面会更好写一点。

        然后,就是求时间了。先把 b 加上已经取模好的 s ,然后 a 加上( b 模 60)(也就是把 a 加上多出来的多少小时),再把 b 除以 60 ,也就是现在的分钟数。最后判断 a 是否大于 24 ,大于 24 就减去 24

        最后,就求出了 a 和 b 的值。

        当然,有50%的数据时b+s小于59,只要b+s就可以了,不用作更多的运算。

代码

#include<bits/stdc++.h>
using namespace std;

int main(){
	int a,b;
	long long s;
	cin>>a>>b>>s; 
	s%=1440;
	b+=s;
	a+=b/60;
	b%=60;
	if(a>=24){
		a-=24;
	}
	cout<<a<<" "<<b;
	return 0;
}

二、日月如梭

题目背景

        光阴似箭,日月如梭。人生短暂,时间宝贵,我们要在有限的时间里去做更多有意义的事情。

题目描述

        这不,在一年一度的班长选举中,小明、小红和小李都想要竞争班长。

        经过一轮激烈的角逐,最终小明、小红和小李都得到了一定数量的选票,分别为 a、b 和 c 张。

        你需要计算,对于每一个班长候选人,他需要再得到多少张选票才能成为班长呢?

        当上班长的条件为,得到的选票数要超过其他两位候选人的选票数。

输入格式

        一行三个整数 a,b,c,分别表示小明、小红和小李得到的选票数。

输出格式

        一行三个整数,分别表示小明、小红和小李需要再得到的选票数。

样例数据

输入样例 #1
12 13 17
输出样例 #1
6 5 0

数据范围

对于 20% 的数据,a=b=c。

对于 50% 的数据,a≤b≤c。

对于 100% 的数据,0≤a,b,c≤105。

解析&代码

20pt

        因为a=b=c,那么每个人只要再投一票就可以当选班长,直接输出1 1 1。所以我们可以这样:

#include<bits/stdc++.h>
using namespace std;

int main(){
	long long a,b,c;
	cin>>a>>b>>c;
	if(a==b&&b==c){
		cout<<"1 1 1";
	}
	return 0;
}

50pt

        因为 a≤b≤c ,那么我们可以找出最大的那个数,然后再把其他两个数与这个最大的数相减的差加1,而最大那个数不用再投票就能竞选班长。(这么算是因为其他两个人再投到与最大那个数一样时,再投一票就可以竞选班长)。所以我们可以得出以下代码:

#include<bits/stdc++.h>
using namespace std;
int maxx(int x,int y,int z){
	if(x>y) swap(x,y);
	if(y>z) swap(y,z);
	if(x>y) swap(x,y);
	return z;
}
int main(){
	long long a,b,c;
	cin>>a>>b>>c;
	if(a==b&&b==c){
		cout<<"1 1 1";
		return 0;
	}
	int num=maxx(a,b,c);
    int numa=0,numb=0,numc=0;
	if(num==a){
        numb=num-b+1;
        numc=num-c+1;
    }else if(num==b){
        numa=num-a+1;
        numc=num-c+1;
    }else{
        numb=num-b+1;
        numa=num-a+1;
    }
	cout<<numa<<" "<<numb<<" "<<numc;
	return 0;
}

100pt

        那么剩下我们就要考虑:当a=b=c时;当有两个数相等时;当每个数都不想同时。从这三个方面考虑。而且我们要在分支一下:在有两个数相等时,这两个相等的数是否是小于第三个数,还是大于第三个数?如果这些你都考虑到了,那么你真是个细心的程序员。

        所以代码就如下:

#include<bits/stdc++.h>
using namespace std;
int maxx(int x,int y,int z){
	if(x>y) swap(x,y);
	if(y>z) swap(y,z);
	if(x>y) swap(x,y);
	return z;
}
int main(){
	long long a,b,c;
	cin>>a>>b>>c;
	if(a==b&&b==c){
		cout<<"1 1 1";
		return 0;
	}
	int num=maxx(a,b,c);
	int numa=0,numb=0,numc=0;
	if(a==b||a==c||b==c){
		if(a==b){
			if(a<c){
				numa=c-a+1;
				numb=numa;
			}else{
				numa=numb=1;
				numc=a-c+1;
			}
		}else if(b==c){
			if(b<a){
				numb=a-b+1;
				numc=numb;
			}else{
				numc=numb=1;
				numa=c-a+1;
			}
		}else{
			if(c<b){
				numc=b-c+1;
				numa=numc;
			}else{
				numc=numa=1;
				numb=a-b+1;
			}
		}
	}else{
		if(num==a){
			numc=a-c+1;
			numb=a-b+1;
		}else if(num==b){
			numa=b-a+1;
			numc=b-c+1;
		}else{
			numa=c-a+1;
			numb=c-b+1;
		}
	}
	cout<<numa<<" "<<numb<<" "<<numc;
	return 0;
}

三、倒水

题目描述

        现给你 2 个水杯,容量分别为 a 和 b。最开始时,1 号水杯和 2 号水杯都是空的。

        现你要按照如下规则操作倒水 k 次:

  • 如果 1 号杯子水是满的,则将 1 号杯子中的水全部倒掉。
  • 否则如果 2 号杯子是空的,则将 2 号杯子装满水。
  • 否则,将 2 号杯子的水全部倒入 1 号杯子,直到 1 号杯子装满水或 2 号杯子水为空。

        请问 k 次操作后,1 号杯子和 2 号杯子中的水量分别是多少?

输入格式

        一行三个整数 a,b,k,分别表示 1 号杯⼦的容量,2 号杯子的容量和操作次数。

输出格式

        一行两个整数,分别表示 1 号杯子和 2 号杯子中的水量。

样例数据

输入样例 #1
3 5 2
输出样例 #1
3 2

        第一次操作,给 2 号杯子装满水,水量分别为 0,5; 第二次操作,将 2 号杯子的水全部倒入 1 号杯子,直到 1 号杯子装满水,水量分别为 3,2。

输入样例 #2
2 6 3
输出样例 #2
0 4

数据范围

对于 20% 的数据,k=1。

另有 30% 的数据,b 是 a 的倍数。

对于 100% 的数据,1≤a,b≤105,1≤k≤105。

解析&代码

20pt

        因为k=1,说明只要进行一次操作。那么我们就直接知道b杯子为空杯子,只要b杯子装满水就可以了。那么直接输出0和b,所以代码如下:

#include<bits/stdc++.h>
using namespace std;

int main(){
	long long a,b,k;
	cin>>a>>b>>k;
	cout<<0<<" "<<b;
	return 0;
}

30pt

        (见100分代码,因为这跟100分只差了一点,我就懒得写了)

100pt

        我们可以建立两个变量numa和numb,分别表示a水杯的水量和b杯子的水量。然后进行k次操作。(一点一点来)

        先是判断a杯子是否是满的,满的话则numa=0(把a中的水倒掉)。

        然后再判断b杯子是否为空的,空的话则numb=b(把b杯子装满)。

        最后也就是最关键的,题目说直到a水杯满了或b水杯空为止。那么,我们就得想:a-numa(也就是a水杯所空的位置)的值是否能被numb(b水杯里现在有的水)给倒满。如果能倒满,那么numa=a(杯子a被灌满),而b则减去a-numa(也就是把b水杯里的水倒进a水杯所空的位置所剩下的);但是如果倒不满,则a+=numb,b水杯里的水就为空了。

        如果可以的话,在进行这些判断时可以直接判断a水杯是否满了或者b水杯是否空了,只要满足其中一个条件,就continue。

        所以代码就如下了:

#include<bits/stdc++.h>
using namespace std;

int main(){
	long long a,b,k;
	cin>>a>>b>>k;
	int numa=0,numb=0;
	for(int i=1;i<=k;i++){
		if(numa==a){
			numa=0;
		}else if(numb==0){
			numb=b;
		}else{
			if(numa==a||numb==0){
				continue;
			}
			if(a-numa>numb){
				numa+=numb;
				numb=0;
			}else{
				numb-=a-numa;
				numa=a;
			}
		}
	}
	cout<<numa<<" "<<numb;
	return 0;
}

四、硬币

题目描述

        小明同学是一个硬币收集者,收集了很多不同面值的硬币。

        现在他把这些硬币分成了两堆,已知两堆硬币分别有 n 和 m 枚硬币。

        第一堆里有 n 枚面值为 b1,b2,…,bn 的硬币。第二堆里有 m 枚面值为 c1,c2,…,cm 的硬币。现在小明同学想要从两堆硬币中各选择一枚硬币,使得两枚硬币的面值之和 不超过 k。

        请你帮助小明同学计算一下他有多少种选择方案。 不同的硬币组合,就算面值相同,也视为不同的方案。

输入格式

        第一行三个整数 n,m,k,分别表示第一堆硬币的数量,第二堆硬币的数量,以及面值之和的上限。

        第二行 n 个整数 b1,b2,…,bn,表示第一堆硬币的面值。

        第三行 m 个整数 c1,c2,…,cm,表示第二堆硬币的面值。

输出格式

        输出一个整数,表示小明同学有多少种选择方案。

样例数据

输入样例 #1
4 4 8
1 5 10 14
2 1 8 1
输出样例 #1
6

        可选方案有: (1,2),(1,1),(1,1),(5,2),(5,1),(5,1),共 6 种。

输入样例 #2
2 3 4
4 8
1 2 3
输出样例 #2
0

数据范围

对于 30% 的数据,1\leq n,m\leq 10001\leq b_{i},c_{i}\leq 1000

对于 60% 的数据,1\leq n,m\leq 10^{5}1\leq b_{i},c_{i}\leq 1000

对于 100% 的数据,1\leq n,m\leq 10^{5}1\leq b_{i},c_{i}\leq 10^{5}1\leq k\leq 2\times 10^{5}

解析&代码

 30pt

        用普通的方法,那就是一个一个循环遍历过去,这个方法比较简单,我就不再多说了。

        代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n,m,k,Sum,b[10000000],c[10000000];
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	for(int i=1;i<=m;i++){
		cin>>c[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(b[i]+c[j]<=k){
				Sum++;
			}
		}
	}
	cout<<Sum;
	return 0;
}

50pt

        我们可以换个思路,把b数组和c数组进行排序。然后再一次次的遍历,只要相加小于k,num就加一,如果相加大于k,则跳出本次循环(因为已经排好序了,后面的数再怎么加都大于k),并让Sum加上本次循环的num,也就是符合的方案,然后再进行下一次循环。

        所以代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n,m,k,Sum,b[10000000],c[10000000];
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	sort(b+1,b+1+n);
	for(int i=1;i<=m;i++){
		cin>>c[i];
	}
	sort(c+1,c+1+m);
	for(int i=1;i<=n;i++){
		int num=0;
		for(int j=1;j<=m;j++){
			if(b[i]+c[j]>k){
				break;
			}
			num++;
		}
		Sum+=num;
	}
	cout<<Sum;
	return 0;
}

100pt

        如果还是像上面一样的话,则会TLE,那么我们可以用二分查找来解决问题。

        第一步用Sort排序,将两堆硬币按从小到大顺序分别排一下,

        第二步 for循环遍历其中一堆硬币a,再用定值k减去我所遍历的硬币值得到s,

        第三步 在另一堆硬币b当中用二分法找到满足小于等于s的那个硬币最大值所在的位置l,找到之后,那么我们算一下对于当下遍历这一堆a当中的这个硬币值,另一堆b当中能有多少种组合,就是这个l位置往前的,其实就是l种组合。(这个思路是我老师提供的)

        所以代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n,m,k,t,mid,l,r,Sum,b[10000000],c[10000000];

int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	sort(b+1,b+1+n);
	for(int i=1;i<=m;i++){
		cin>>c[i];
	}
	sort(c+1,c+1+m);
	c[m+1]=c[m];
	for(int i=1;i<=n;i++){
		t=k-b[i];
		l=1;
		r=m+1;
		if(t>0){
			while(l<r){
				mid=(l+r)/2;
				if(t>=c[mid]){
					l=mid+1;
				}else{
					r=mid;
				}
			}
		}
		Sum+=l-1;
	}
	cout<<Sum;
	return 0;
}

小结

        这次比赛比较简单,分数线为100分,所以这几道题比较简单,如果有不懂的或哪个细节没写清楚,欢迎在评论区里留言。感谢支持与鼓励!!!

  • 32
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值