(题目更新啦)第十六届蓝桥杯模拟赛(第一期)个人题解

792c28ffbd0545cd8d24706248f35427.png林专大一牲第一次写blog,更新较慢,写的不好的地方请见谅(好多题目忘记了题号 and 暂时没有题目要求的内容…后面会补充的!)

本次带来的是蓝桥杯模拟赛第一期的个人题解(笨人水平较低,大家可以在评论区指出错误/讨论更优解~) 大多题我是用c++写的,但也掺了几道c,为以后全面用c++打比赛作过渡!

填空题

T4 日期问题(模拟)

cd17bc84a12948ccac16760c7e9fbb7e.png

请问从1901年1月1日到2024年12月31日共有多少个一好日期 

 

(意难平)

日期问题嘛,直接就能想到把日期视为八位数(如2024/11/16看作20241116这个数)。循环遍历判断是否满足要求。然后就可以开始愉快地写代码了

在check_valid函数中,首先判断day是否满足1、21、31(即day%10==1) 不满足就return false;后面判断日期是否合法就是很自然的写法。

再然后就会遇到一个问题:check_good函数里怎么判断某一天是不是周一?在草稿纸上举几个例子就不难想到,已知1901.1.1.为星期二,和这一天的天数差day_gap%7==6的话那这天就是周一(距离最近的就是1901.1.21.)

天数差=年份差*365+闰年补充天数+当年与1.1.相差天数

(在check_good函数里,从1901年到目标年份的前一年,每遇到一个闰年天数差就要多一天)

笨人在考场上计算天数差时漏掉了年份差*365这一部分,也没检查就自信提交了(大哭

这么一道填空就水灵灵地爆掉了…血的教训(以后一定注意!!)

下面是赛后微修代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int days[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
//计算某天与1月1日的天数差
//switch里不写break,利用找到入口后一直执行cnt+=
int caculate_days(int year,int month,int day)
{
	int cnt=0;
	switch(month)
	{
		case 12:cnt+=30;
		case 11:cnt+=31;
		case 10:cnt+=30;
		case 9:cnt+=31;
		case 8:cnt+=31;
		case 7:cnt+=30;
		case 6:cnt+=31;
		case 5:cnt+=30;
		case 4:cnt+=31;
		case 3:if(year%100&&year%4==0||year%400==0)cnt++;
		cnt+=28;
		case 2:cnt+=31;
		case 1:cnt+=day-1;
	}
	return cnt;
}
//判断日期是否合法
bool check_valid(int year,int month,int day)
{
	if(day%10!=1) return false;
	if(month==0||month>12) return false;
	if(day==0||month!=2&&day>days[month]) return false;
	if(month==2)
	{
		//leap来控制平/闰年(在day>28+leap体现),leap在右式为真即为1,右式为假即为0
		int leap=year%100&&year%4==0||year%400==0;
		if(day>28+leap) return false;
	}
	return true;
}
//判断日期是否是好的周一
bool check_good(int year,int month,int day)
{
	int st=1901;
	//初始化天数差并补充在year之前的所有闰年
	int day_gap=(year-1901)*365%7;
	for(st;st<year;st++)
	{
		if(st%100&&st%4==0||st%400==0) day_gap++; 
	}
	//当年如果是闰年且在二月份之后,不需要再补充一天
	//因为在caculate_days函数里已经进行了这次判断
	day_gap%=7;
	day_gap+=(caculate_days(year,month,day)%7);
	day_gap%=7;
	if(day_gap==6) return true;
	return false;
}
int main()
{
	int res=0;
	//把年月日看作八位数,模拟每个八位数检查其是否满足所有条件
	for(int date=19010101;date<=20241231;date++)
	{
		int year=date/10000;
		int month=date%10000/100;
		int day=date%100;
		if(check_valid(year,month,day)&&check_good(year,month,day))
		{
			res++;
		}
	}
	printf("%d\n",res);
	return 0;
}

这个程序的结果为762

编程题

T?(脑筋急转弯)

bbd8eac4fdf3467f9695224b18735fe4.png

 

刚看到这道题我直接在想要用分离数位再处理还是直接用字符读入…(明显做题有点赶) 

然后突然就发现这题只需要找到最大的那一个数字就行了(比如82020009,起决定性作用的是9)

这个就话不多说,上代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int main()
{
	long long n;
	cin>>n;
	int maxv=-1;
	while(n)
	{
		int t=n%10;
		maxv=max(maxv,t);
		n/=10;
	}
	cout<<maxv;
	return 0;
}

T9 求满足要求 和的最大值

a4b6f295cac14314b8130cb3e062b412.png

 

注意到题目一定是每隔一个数加一次,所以可以求奇数列和偶数列分别满足条件的最大值,最后再取这两种情况中的最大值。

代码实现中只需先把res初始化为前k个数的和,用tmp表示当前k个数的和。在循环中tmp加上新数减去旧数,然后更新res即可。线性扫一遍时间复杂度是O(n),时间很充裕。注意数据范围(现在有点记不清了,等有题目再详细说下怎么推),很容易推出来res是有可能爆int的,所以res和tmp全都用long long存就可以了。下面是代码实现:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N=100010;
int n,k;
int a[N];

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	scanf("%d",&k);
    //求奇数列中和的最大值res1
	long long res1,tmp=0;
    //初始化res1为奇数列最前面k个数之和
	for(int i=1;i<=2*k-1;i+=2) tmp+=a[i];
	res1=tmp;
	for(int i=2*k+1;i<=n;i+=2)
	{
		tmp+=a[i]-a[i-2*k];
		res1=max(res1,tmp);//更新res1
	}
	tmp=0;
    //求偶数列中和的最大值res2
	long long res2;
    //初始化res2为偶数列最前面k个数之和
	for(int i=2;i<=2*k;i+=2) tmp+=a[i];
	res2=tmp;
	for(int i=2*k+2;i<=n;i+=2)
	{
		tmp+=a[i]-a[i-2*k];
		res2=max(res2,tmp);
	}
	long long res=max(res1,res2);
	printf("%lld",res);
	return 0;
}

T10(DP)最大的勾

10655beb2c3844acbf6e3d59045b2a2a.png

 

当时就莫名感觉有点最长上升子序列的影子(当然我也是这么处理的)

这道题我是这样考虑的:把这个问题拆分成某元素左边的最长递减子序列长度+右边的最长递增子序列长度(用动态规划就能做到这一点,且时间复杂度为O(n^2)。预处理完dp1[]和dp2[]后,遍历a[]中每个元素,并更新满足要求的最长答案(不要忘记加上这个元素本身)

聪明的小伙伴肯定发现了,这个显然是有点小问题的。也确实,提前一个半小时我就交卷了…交完就灵光一闪(bushi)忘记了题目中有个要求是: 最后那个数是最大的(哈哈哈其实是闹了乌龙,只是要最长滴)

下方DP应该是满分!

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N=1010;

int n;
int a[N];
int dp1[N],dp2[N];
//dp1[i]表示a[i]前严格递减子序列的最长长度
//dp2[i]表示a[i]后严格递增子序列的最长长度

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	//预处理dp1[]
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(a[j]>a[i]) dp1[i]=max(dp1[i],dp1[j]+1);
		}
	}
	//预处理dp2[]
	for(int i=n;i>=1;i--)
	{
		for(int j=n;j>=i;j--)
		{
			if(a[j]>a[i]) dp2[i]=max(dp2[i],dp2[j]+1);
		}
	} 
	//遍历元素,其前最长递增子序列+其后最长递增 +1(其自身)即为所求
	int res=0;
	for(int i=1;i<=n;i++)
	{
		res=max(res,dp1[i]+dp2[i]+1);
	}
	cout<<res;
	return 0;
}

 

总结

总体来说题目难度适中,也没有用到太多算法知识,题目随题号基本由易到难。

在写代码之前还是要先想清楚一些细节,不然写代码的时候很可能就会漏掉那一丢丢,最后导致重大事故(bushi),尤其是填空题

还有就是考场上的每一分钟都很重要,还是要好好检查代码的(不要太提前交卷!),写一遍就过but没有考虑全面的时候很多!最好是全做完再来仔细看遍题目,想下代码有没有完整实现要求,再举出一些极端情况/一般情况的样例进行调试(比如T10)

这是一个全新的开始,平时还是要多做题,找灵感。争取在下一次比赛中稳扎稳打,改掉本次比赛出现的问题。

 

未完待续…

 

 

 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值