林专大一牲第一次写blog,更新较慢,写的不好的地方请见谅(好多题目忘记了题号 and 暂时没有题目要求的内容…后面会补充的!)
本次带来的是蓝桥杯模拟赛第一期的个人题解(笨人水平较低,大家可以在评论区指出错误/讨论更优解~) 大多题我是用c++写的,但也掺了几道c,为以后全面用c++打比赛作过渡!
填空题
T? 日期问题(模拟)
(意难平)
日期问题嘛,直接就能想到把日期视为八位数(如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++;
}
//当年如果是闰年且在二月份之后,再补充一天
if(year%100&&year%4==0||year%400==0)
{
if(month>2) day_gap++;
}
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?(脑筋急转弯)
刚看到这道题我直接在想要用分离数位再处理还是直接用字符读入…(明显做题有点赶)
然后突然就发现这题只需要找到最大的那一个数字就行了(比如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;
}
T10(DP)
当时就莫名感觉有点最长上升子序列的影子(当然我也是这么处理的)
这道题我是这样考虑的:把这个问题拆分成某元素左边的最长递减子序列长度+右边的最长递增子序列长度(用动态规划就能做到这一点,且时间复杂度为O(n^2)。预处理完dp1[]和dp2[]后,遍历a[]中每个元素,并更新满足要求的最长答案(不要忘记加上这个元素本身)
聪明的小伙伴肯定发现了,这个显然是有点小问题的。也确实,提前一个半小时我就交卷了…交完就灵光一闪(bushi)忘记了题目中有个要求是: 最后那个数是最大的!
显然考场上写的代码没有考虑这一点orz(只希望数据别太卡这一个要求吧…这也提醒我下次要细心,不要写代码投入到最后漏掉部分条件)(也可能读题不够仔细(划掉
我抽空看看再把漏掉的要求补上吧
下方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)
这是一个全新的开始,平时还是要多做题,找灵感。争取在下一次比赛中稳扎稳打,改掉本次比赛出现的问题。