2024Goduck csp-x复赛模拟2补题报告

前言:

又是一个夜黑风高的白天,Coduck又双叒叕进行了一次模拟考。

第四题AC,第一二三题分别得60,50,40,总分250。当然我不是二百五……

怎么说呢好歹进步了一点不是吗?

作者考试过程:拿到试卷,看题,哇塞好简单,做做做做做做做做做做做做做,昨晚第一个题,看第二个题,哇塞好简单,做做做做做做做做做做做做做…………………………做完第四个题,保存,唉不小心把第三题删了,把第三题补完了,就到了找八哥的时间,结果一看自己的代码,怎么看怎么完美(摘自教练话语)。然后找着找着呢,啊发现不对,改完然后愣神额,最后就提交咯。

这四个题有三个都是时间超限的……服了

第一题《独木桥》bridge

题目描述

长度为 L 米的独木桥上有 n 个人,他们每个人都想以最快的时间离开危险的独木桥。已知每个人在独木桥上的行走速度为 11 米 / 秒 ,每个人只要能走到独木桥的两个端点中的其中一个就可以离开独木桥。
由于独木桥的桥面宽度很窄,只能容纳一个人通过,当两个人相遇时,他们无法交错通过,只能各自调转方向,继续沿反方向行走。
给你独木桥上的人数n ,独木桥的长度 L , 第 i 个人的初始位置到独木桥左端点的距离a​i​​ 米(每个人开始的朝向未知,但他们可以根据需要随时调转行走的方向)。
请计算出所有人同时出发,全部都离开独木桥所需的最短时间。

输入格式(输入文件为 bridge.in)

第一行一个整数 n ,表示人数。
第二行一个整数 L ,表示独木桥的长度(米)。
第三行是1a​1​​,2a​2​​ a​n​​ ,其中 a​i​​ 表示第 i 个人初始位置到独木桥左端点 的距离。

输出格式(输出文件为 bridge.out)

输出一行一个整数,表示所有人都离开独木桥所需的最短时间。

输入样例 #1
3
10
2 6 7
输出样例 #1
4
样例一说明

说明:三个人同时出发,第一个人向左走,需要 22 秒离开桥,第二个人向右走需要44秒离开桥,第三个人向右走需要33秒离开桥。所以,44秒后,三个人都离开了独木桥。

输入样例 #2
7
214
11 12 7 13 176 23 191
输出样例 #2
38

太简单了~(老师语)

正解思路:

从小到大排序所有人到左端点的距离,再找出最大的最小的人左右端点的距离,最后输出就行了。

#include<bits/stdc++.h>
using namespace std;
long long n,l,xx;
long long maxx;
int main(){
	cin>>n;//人的数量
	cin>>l;//桥的长度
	for(int i=1;i<=n;i++){
		cin>>xx;//当前人到左端点的距离
		maxx=max(maxx,min(xx,(l-xx)));//maxx赋值最大的最小的xx到左右端点的距离
	}
	cout<<maxx;//输出
	return 0;
} 

那么我当时是怎么想的呢?用了一个while循环判断各种情况,结果时间超限

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
long long  n,len,a[1000005],sj=0;
bool lk(){
	for(int i=1;i<=n;i++){
		if(a[i]>0&&a[i]<len){//离开的定义就是==0or==len长度
			return 1;
		}
	}
	return 0;
}
int main(){
	//freopen("bridge.in","r",stdin);
	//freopen("bridge.out","w",stdout);
	cin>>n>>len;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	while(lk()==1){//全部离开就结束
		for(int i=1;i<=n;i++){
			if(a[i]<=len/2&&a[i]>0){//思路就是,如果这个人再桥中间靠做的位置并且没有出去
				a[i]--;//那么距离左端点的距离减去1,即左走一步
			}
			else if(a[i]<len&&a[i]!=0){//除了以上情况就实在右边,判断还在桥上
				a[i]++;//往右走进一步
			}
			if(a[i]==a[i-1]&&a[i]!=0&&a[i]!=len){//判断如果这两个人要相遇了,那么分别向两端走,后来老师讲才知道这是一个废条件
				a[i]++;
				a[i-1]--;
			}
		}
		sj++;
	}
	cout<<sj;
	//fclose(stdin);
	//fclose(stdout);
	return 0;
} 

第二题《移动棋子》chess

题目描述

一维的棋盘上有无限多个格子,每个格子都有一个编号,最中间的格子编号为 00 ,00 号格子向右依次编号为 11,22,33,… ,向左依次编号为 −1−1, −2−2, −3−3,… 。
小明的目标是要将一枚棋子从 x 号格子移动到 y 号格子。
每一次操作有两种选择:
操作 11 :向右移动 11 个格子。
操作 22 :从当前棋子所在的 a 号格子,直接跳到 −a 号格子(如:可以从 66 直接跳到 −6−6 ,也可以从 −6−6 直接跳到 66 )。
可以证明,无论整数 x 和 y 的值是多少,目标总是可以实现的。
请你设计程序,帮小明计算把棋子从 x 号格子移动到 y 号格子需要的最少操作次数。

输入格式(输入文件为 chess.in)

一行,两个整数 x 和 y , 表示要将棋子从 x 号格子移动到 y 号格子。

输出格式(输出文件为 chess.out)

一个整数,表示小明把棋子从 �x 号格子移动到 �y 号格子需要的最少操作次数。

输入样例 #1
10 20
输出样例 #1
10
样例一说明

说明:需要进行1010次操作11。

输入样例 #2
10 -10
输出样例 #2
1
样例二说明

需要进行1次操作2

输入样例 #3
-10 -20
输出样例 #3
2
样例三说明

说明:先进行 11 次操作 22 ,然后进行 1010 次操作 11 ,最后进行 11 次操作 22 。

自述:

这一道题我最初的思路也是用while循环,每一次循环判断六种状况,再根据各个状况进行输出,so因此时间超限。正解的思路就是用规律求出的得数,首先分出来三种,1是xy都是负数,2都是整数,或者一正一副,再根据这三个延伸出来其余方案,此处直接在代码讲解。

#include<bits/stdc++.h>
using namespace std;
int main(){
	long long xx,yy,ans=0;
	cin>>xx>>yy;
	if(xx*yy<0){//负数
		ans=abs(abs(xx)-abs(yy))+1;//他们两个的绝对值+1
	}
	else if(xx*yy>0){//正
		if(xx>yy){//起点大于重点
			ans=xx-yy+2;他们两个的距离加上2次操作,解释一下:两种操作分别是向右和取反,可是x大的话怎么可能往右走到终点?那么就只能先变成负数,再向右,取到合适值之后再取绝对值,那么就是加的差值再加上两次取反就可以了
		}
		else if(xx<yy){//小于的话直接取差值
			ans=yy-xx;
		}
	}
	else{
		ans=abs(xx-yy);//取差
		if(xx>yy){
			ans=ans+1;
		}
	}
	cout<<ans<<endl;
	return 0;
}

下面是我的代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
long long  xx,yy,bs=0;
int main(){
	freopen("chess.in","r",stdin);
	freopen("chess.out","w",stdout);
	cin>>xx>>yy;
	while(xx!=yy){
		//if(xx<0&&yy>0||xx>0&&yy<0){
		//	xx=~xx+1;
		//}
		if(xx>0&&yy>0){
			xx++;
		}
		else if(yy<0&&xx<0){
			xx=~xx+1;
		}
		else if(yy<0&&xx>0&&xx!=abs(yy)){
			xx++;
		}
		else if(yy<0&&xx>0&&xx==abs(yy)){
			xx=~xx+1;
		}
		else if(xx<0&&yy>0&&abs(xx)<=yy){
			xx=abs(xx);
		}
		else if(xx<0&&yy>0&&abs(xx)>yy){
			xx++;
		}
		bs++;
	}
	cout<<bs;
	fclose(stdin);
	fclose(stdout);
	return 0;//就不解释了,这个代码是时间超限的
} 

第三题《动物园》zoo

题目描述

某动物园里有n个场馆和m种动物(m≤n)。

n个场馆的编号分别用 1,2,3,..,n 表示;m种动物的编号分别用 1,2,3,..,m 表示。每一个场馆中只饲养了一只动物,不同的场馆可能饲养着相同种类的动物。

这个动物园的门票比较特殊,游客在购买门票时必须说明要参观的场馆的起止编号a和b(起止编号会打印到游客购买的门票上),代表游客只能参观动物园的第a个场馆至第b个场馆(包含 a,b)里的动物,其他的场馆不能去。门票按一个场馆十元收费。

如果你购买的门票的起止场馆编号是 33 到 88,那么你需要花 6060 元钱购买门票,只能观看3,4,5,6,7,83,4,5,6,7,8 号场馆的动物。

小明希望看到动物园内所有种类的动物,同时小明是个非常节约的孩子,他希望花最少
的钱买门票。 请你帮小明计算:他最少需要花费多少钱买门票才能看到所有种类的动物(同
一种动物他可能不止看一个)。注意:小明只能买一张门票。

输入格式(输入文件为 zoo.in)

第一行两个整数 n,m,分别表示动物园内的场馆数量及动物种类数量。

第二行是 x​1​​,x​2​​,⋯,x​n​​,其中x​i​​表示第i个场馆中的动物种类编号。

输出格式(输出文件为 zoo.out)

一行一个整数p,表示小明的门票费用。

输入输出样例

样例 1 输入

12 5
2 5 3 1 3 2 4 1 1 5 4 3

*样例 1 输出

60

样例 11 说明:花费最少的其中一种购票方案选择是 a=2,b=7 ,表示购买场馆 2,3,4,5,6,72,3,4,5,6,7的门票,分别看到的动物是5,3,1,3,2,45,3,1,3,2,4,其中动物33小明看了两个。

自述:

这个题我用了一个非常作死的做法,用了一个双层for循环:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
long long  n,m,a[1000005],pmin=1000000,ans=0;
bool mp(int ll,int rr){
	int b[100000]={0};
	for(int i=ll;i<=rr;i++){
		b[a[i]]=1;
	}
	for(int i=1;i<=m;i++){
		if(b[i]==0){
			return 0;
		}
	}
	return 1;
}
int main(){
	freopen("zoo.in","r",stdin);
	freopen("zoo.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		if(n-i+1<m) break;//这里我想着尽量减轻代码的负担
		for(int j=i;j<=n;j++){
			if((j-i+1)>=m){//也是减轻一丢丢
				if(mp(i,j)==1){
					//cout<<"i:"<<i<<" j:"<<j<<endl;
					ans=(j-i+1)*10;
					//cout<<"ans:"<<ans<<endl;
					pmin=min(pmin,ans);
					if(pmin==m*10){//m种动物至少需要10m的钱,所以如果提前找到那么肯定确定这个是至少要花费的钱
						cout<<pmin;
						return 0;
					}
				}
			}
		}
	}
	cout<<pmin;
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

but,but———时间复杂度还是高。所以这个题就可以用D2学的双指针优化。

众所周知唉~双指针有两种。第一种是一个在头,一个在尾巴的位置,双方往中去。第二种就是都在头,都向尾巴去。

#include<bits/stdc++.h>
using namespace std;
long long n,m,a[1000005],vis[1000005];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)  cin>>a[i];
	long long l=1,r=0,cnt=0,ans=0x3f3f3f3f;//0x3f3f3f3f是极大值,2^31-1
	while(r<n){//往尾巴那里去,如果大于了直接结束
		while(cnt<m&&r<n){//目前可以看的cnt种动物小于总动物数
			r++;//退
			vis[a[r]]++;//这个动物的种数加上一
			if(vis[a[r]]==1) cnt++;//可观种数加一
		}
		while(cnt==m){//可观种数要是所用动物种数
			ans=min(ans,r-l+1);//刷新ans
//			cout<<r<<"<<"<<l<<endl;
			vis[a[l]]--;//l要进位了,那么进位的话这一位不就不能算cnt了吗
			if(vis[a[l]]==0) cnt--;//如果这一位没有了,可观测下降
			l++;//进位
		}
	}
	cout<<ans*10;//一个馆要10块钱
	return 0;
}

第四题《摧毁》destroy

题目描述

坐地日行八万里,巡天遥看一千河。

20772077年,人类不仅仅是赛博科技得到了发展,太空技术也已经得到了极大的发展。地球的不同外轨道上已经充斥着各种功能用途的人造卫星。因为一个轨道上的卫星数量是有上限的,且卫星更新换代速度很快,如果想要发射新的卫星,需要把所有旧的卫星摧毁。

人类有两种不同的武器可以摧毁卫星,具体如下(其中PW为新的能量单位):
(1)使用定点激光武器花费 1 PW 的代价摧毁任意轨道上指定的一个卫星。
(2)使用脉冲轨道武器花费 c PW 的代价把某一轨道上的所有卫星摧毁。

现在有n个旧卫星分布在不同的外轨道上,你的任务是摧毁这些旧卫星。给出这n个卫
星的轨道编号,求将这些卫星全部摧毁的最小代价是多少?

输入格式( 输入文件名为 destroy.in)

第一行一个正整数T,表示测试数据组数。

接下来对于每组测试数据(注意:每组测试数据有22行数据,以下共2∗T行数据):

第一行两个正整数n和c表示需要摧毁的卫星数量和使用脉冲轨道武器的代价。

第二行 是,x​1​​,x​2​​,⋯,x​n​​,其中x​i​​表示第i个卫星的轨道编号。

输出格式( 输出文件名为 destroy.out)

输出T行答案,对于每组测试数据,输出一行一个整数表示摧毁所有卫星的代价。

输入输出样例

样例 1 输入

4
10 1
2 1 4 5 2 4 5 5 1 2
5 2
3 2 1 2 2
2 2
1 1
2 2
1 2

样例 1 输出

4
4
2
2

样例说明: 对于第一组测试数据,使用脉冲武器的代价为 1 PW。轨道 11 上有 22 个卫星,轨道 22 上有 33 个卫星,轨道 44 上有 22 个卫星,轨道 55 上有 33 个卫星。因此对于轨道 11、22、44、55,均使用脉冲武器各花费 1PW 的代价可全部摧毁,总的代价为 44 PW,很显然该方案为总代价最小方案。

对于第二组测试数据,使用脉冲武器的代价为 2 PW。轨道 11 上有 11 个卫星,轨道 22 上有33 个卫星,轨道 33 上有 11 个卫星。因此,对于轨道 11 采用激光武器,轨道 22 采用脉冲武器,轨道 33 采用激光武器可全部摧毁所有卫星,总的代价为 4 PW,很显然该方案使得总代价最小。

自述:

啊这个题是我唯一AC的题,所以下面的代码也完全是我的

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
long long   t,n,a[1000010],c;
int main(){
	freopen("destroy.in","r",stdin);
	freopen("destroy.out","w",stdout);
	cin>>t;
	while(t--){//循环t次
		cin>>n>>c;
		int dj=0,xx;
		for(int i=1;i<=1000005;i++){
			a[i]=0;//每次循环赋初值
		}
		for(int i=1;i<=n;i++){
			cin>>xx;//利用桶排序
			a[xx]++;
		}
		for(int i=1;i<=1000005;i++){
			if(a[i]>=1){
				dj+=min(a[i],c);//代价累加,选激光和镭射炮代价较小值
			}
		}
		cout<<dj<<endl;
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

总结

总之呢,这次还是提升了不少的分数,虽然关于时间超限这一点没有实现判断好,所以我……(懒得写了,省略几个字)。那么愿我们出的成绩如累加器,步步高升!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值