2023蓝桥杯省赛B组真题及解析
2023蓝桥杯省赛B组真题及解析
7.子串简写
算法:前缀和
https://www.lanqiao.cn/problems/3514/learning/?subject_code=1&group_code=4&match_num=14&match_flow=1&origin=cup
1.日期统计
https://www.lanqiao.cn/problems/3492/learning/?subject_code=1&group_code=4&match_num=14&match_flow=1&origin=cup
算法:dfs剪枝
//找不同时间(找数据)用搜索
#include <iostream>
using namespace std;
int a[100];
bool vis[20240000];
int ans ;
bool check(int date){
if(vis[date])return false;
//如果没访问就访问
vis[date]=true;
//前四位2023满足条件,只需考察月份和日子
int mm=date/100%100;
int rr=date%100;
if(mm<1||mm>12) return false;
if(mm==1||mm==3||mm==5||mm==7||mm==8|mm==10||mm==12){
if(rr>=1&&rr<=31)return true;
return false;
}
if(mm==4||mm==6||mm==9||mm==11){
if(rr>=1&&rr<=30)return true;
return false;
}
if(mm==2){
if(rr>=1&&rr<=28)return true;
}
}
void dfs(int x,int pos,int date)//x表示遍历到a数组中的哪一位,pos表示遍历到8位时间的哪一位
//date为所得时间,所得八位时间用八位数表示如:20230101
{
if(x==100)return ;//题目中100个数字遍历完
if(pos==8) {
//如果8位时间遍历完
if(check(date)) ans++;//找到一个八位数
return ;//递归函数出口
}
//当前a[x]选入date
//前四位有要求2023,后四位月份和日每个都有要求
if(pos==0&&a[x]==2||pos==1&&a[x]==0||pos==2&&a[x]==2||pos==3&&a[x]==3||
pos==4&&a[x]>=0&&a[x]<=1||pos==5&&a[x]>=0&&a[x]<=9||pos==6&&a[x]>=0&&a[x]<=3||
pos==7&&a[x]>=0&&a[x]<=9
){
dfs(x+1,pos+1,date*10+a[x]);//dfs(下一个数,日期中的下一位,日期转化成八位数)
}
//当前a[x]不放入date中 ,也就是date不进行到下一位,而x往后一位
dfs(x+1,pos,date);//dfs(下一个数,日期中的当前位,当前已经选的数字形成部分日期所组成的数 )
}
int main()
{
for(int i=0;i<100;i++)cin>>a[i];
dfs(0,0,0);
cout<<ans;
// 请在此输入您的代码
return 0;
}
法二暴力
//暴力 直接枚举 注意要想能运行需要注意剪枝 把不符合条件及早排除
#include<bits/stdc++.h>
using namespace std;
int num[100];
bool flag[20][40];
int ans;
bool check(int month,int ri){
if(month==1||month==3||month==5||month==7||month==8||month==10||month==12){
if(ri>=1&&ri<=31)return true;
}
if(month==4||month==6||month==9||month==11){
if(ri>=1&&ri<=30)return true;
}
if(month==2){
if(ri>=1&&ri<=28){
return true;
}
}
return false;
}
int main(){
for(int i=0;i<100;i++){
cin>>num[i];
}
for(int a=0;a<100;a++){
if(num[a]!=2)continue;
for(int b=a+1;b<100;b++){
if(num[b]!=0)continue;
for(int c=b+1;c<100;c++){
if(num[c]!=2)continue;
for(int d=c+1;d<100;d++){
if(num[d]!=3)continue;
for(int e=d+1;e<100;e++){
if(num[e]>1)continue;
for(int f=e+1;f<100;f++){
if(num[e]*10+num[f]>12||num[e]*10+num[f]<1)continue;
for(int g=f+1;g<100;g++){
for(int h=g+1;h<100;h++){
int year=num[a]*1000+100*num[b]+10*num[c]+num[d];
int month=10*num[e]+num[f];
int ri=10*num[g]+num[h];
// cout<<year<<"/"<<month<<"/"<<ri<<endl;
// if(year!=2023){//不是2023年
// continue;
// }
if(!check(month,ri)) {//日期不合法
continue;
}
if(flag[month][ri]){//已经有了这一天
continue;
}
flag[month][ri]=true;
ans++;
}
}
}
}
}
}
}
}
cout<<ans;
return 0;
}
高效的方法解决
//高效做法 遍历2023年日期看字符串里有没有满足的!
#include<bits/stdc++.h>
using namespace std;
int a[100];
int d[] ={0,31,28,31,30,31,30,31,31,30,31,30,31};
int ans;
int main(){
for(int i=0;i<100;i++){
cin>>a[i];
}
for(int i=1;i<=12;i++){//月
for(int j=1;j<=d[i];j++) {
int date[]={2,0,2,3,i/10,i%10,j/10,j%10};//日期
int w=0;
for(int k=0;k<100;k++){//去a数组找
if(w==8){
ans++;
break;
}
if(date[w]==a[k]){
w++;
}
}
}
}
cout<<ans;
return 0;
}
3.冶炼金属
https://www.lanqiao.cn/problems/3510/learning/?subject_code=1&group_code=4&match_num=14&match_flow=1&origin=cup
考点是找到公式
(a/(b+1))+1=<V<=a/b
2.01的熵
考点:浮点数精度问题

4.飞机降落
dfs

5.接龙数列
动态规划

6.岛屿个数
考点:BFS
https://www.lanqiao.cn/problems/3513/learning/?subject_code=1&group_code=4&match_num=14&match_flow=1&origin=cup
(有点难)


8.整数删除
考点:优先队列+链表
优先队列:每次方便取最值
链表:此处用的是静态链表且无头结点。方便删除。-->静态链表的删除结点不是真删除而是修改值让这个值不在范围内。
此处的链表为双向链表,开两个数组存放前驱后继下标。

9.景区导游
考点: 图论 LCA(公共祖先问题)+dfs

题目分析:
n个景点对应树上n个结点
n-1条摆渡车对应n-1条树上的边
建树
考点:
树上任意两点的距离path(a,b)=dis[a]+dis[b]-2*dis[lca(a,b)];-->所以手写公共祖先->lca以及配合的dfs->dfs里面在模版基础上增加求到根节点距离
完整代码解析:
//LCA

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
//邻接表
vector<int>e[N];//邻接点
vector<int>t[N];//消耗时间
int dep[N];//深度
int fa[N][21];
long long int ans;//存答案可能超int
int n,k;//n个景点,选中的游览路线里有k个景点
int A[N];//所选路线
long long int dis[N];//每个顶点到根节点的距离即时间
void dfs(int u,int Fa){
dep[u]=dep[Fa]+1;
fa[u][0]=Fa;
for(int i=1;i<=20;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(int i=0;i<e[u].size();i++){
int v=e[u][i];int p=t[u][i];
if(v==Fa)continue;
dis[v]=dis[u]+p;//每个节点都有唯一的父亲,所以用父亲算子节点到根节点的距离
dfs(v,u);
}
}
int LCA(int u,int v){
if(dep[u]<dep[v])swap(u,v);
for(int i=20;i>=0;i--){
if(dep[fa[u][i]]>=dep[v]){
u=fa[u][i];
}
}
if(u==v)return u;
for(int i=20;i>=0;i--){
if(fa[u][i]!=fa[v][i]){
u=fa[u][i];
v=fa[v][i];
}
}
return fa[u][0];
}
//求相邻两点距离
long long int path(int u,int v){
if(u==0||v==0){
return 0;//看绿下标为0的那种不存在的点,到他的距离为0
}
return dis[u]+dis[v]-2*dis[LCA(u,v)];
}
int main(){
cin>>n>>k;
//n-1条摆渡车,邻接表
for(int i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
e[u].push_back(v);
t[u].push_back(w);
e[v].push_back(u);
t[v].push_back(w);
}
//为lca做准备,并求每个所问结点到根节点的距离(时间)
dfs(1,0);
//所问线路
for(int i=1;i<=k;i++){
cin>>A[i];
ans+=path(A[i-1],A[i]);//先存放删除前总时间,即相邻点的距离
}
//删除第i个结点
for(int i=1;i<=k;i++){
cout<<ans-path(A[i-1],A[i])-path(A[i],A[i+1])+path(A[i-1],A[i+1])<<" ";
}
return 0;
}
10.砍树


//核心找u,v公共祖先LCA算法
分段分析代码
LCA
//LCA算法包含两部分:先dfs在求公共祖先
//所需数组:深度dep[ ],找向上的下标fa[当前结点][向上2的多少次方级],邻接表存边 e[u]表示与u相邻的边的另一个点
void dfs(int u,int Fa){//u是当前点,Fa是他父亲的下标
dep[u]=dep[Fa]+1;//他自己的深度是他父亲深度加1
fa[u][0]=Fa;//u的向上2的0次方级是他父亲
//从下往上找祖先
for(int i=1;i<=20;i++){//向上最多2的20方级,因为题目给最多10的5次方个结点。
fa[u][i]=fa[fa[u][i-1]][i-1];//递归:u向上第2的i次方级等价于u向上2的i-1次方所得元素再向上2的i-1次方即2的i-1次方乘2=2的i次方。
}
for(auto&v:e[u]){//当前所给顶点u的所有邻结点
if(v==Fa)continue;
//相邻的这个点不是它父亲再dfs递归,
dfs(v,u);//因为是从最上面的点往下搜,不能重复搜它上面的点,所以他父亲不能再搜。
}
}
//返回值就是u,v的公共祖先
int LCA(int u,int v){//上面dfs是准备工作,下面是核心LCA找u,v公共祖先下标
if(dep[u]<dep[v])swap(u,v);//保证u更深
for(int i=20;i>=0;i--){//u大步向上跳跃
if(dep[fa[u][i]]>=dep[v]){//u的向上2的i次方级的深度大于v的深度
u=fa[u][i];//那么修改u下标为它向上2的i次方级下标
}
}
//所以出循环后u的深度>=v
if(u==v)return u;//u跳完与v相遇那么返回u,说明原来v是u的直系祖先
for(int i=20;i>=0;i--){//两人一起往上大步跳直至祖先相同
if(fa[u][i]!=fa[v][i]){
u=fa[u][i];
v=fa[v][i];
}
}
return fa[u][0];//最终修改的u,v有共同的父亲就是原u,v的公共祖先
}
砍树,利用dfs看砍哪个边
void dfs2(int u,int Fa){//dfs2(当前节点下标,父节点下标)
for(int i=0;i<e[u].size();i++){//u的所有邻接点
int v=e[u][i],p=num[u][i];//v是此时与u相邻的点,p是u此时所在边的编号
if(v==Fa)continue;//不重复搜父亲
dfs2(v,u);
s[u]+=s[v];
if(s[v]==m)ans=max(ans,p);//p是关键边
}
}
主函数
int main()
{
cin>>n>>m;//n个顶点,m组组合不相通
for(int i=1;i<n;i++){//n-1条已知边,等会砍的边只能从这里面选
int x,y;
cin>>x>>y;//一条边的两个顶点
//8~11为邻接表存边,因为是按无向图存所以x,y各弄一次
e[x].push_back(y);//x当前的邻接点y
num[x].push_back(i);//并存放x当前所在边边的标号,因为题目求满足条件的最大边标号
e[y].push_back(x);//同理
num[y].push_back(i);
}
dfs(1,0);//先进行LCA的预备工作dfs利用深搜处理每个点的祖先,从第一个结点开始搜他的父节点默认0.
for(int i=1;i<=m;i++){//核心代码:考察树上差分算法
int a,b;
cin>>a>>b;
s[a]++;
s[b]++;
s[LCA(a,b)]-=2;
}
dfs2(1,0);//从第一个结点开始搜他的父节点默认0.
cout<<ans;//输出答案
// 请在此输入您的代码
return 0;
}
主函数里的核心算法树上差分
树上差分
for(int i=1;i<=m;i++){//核心代码:考察树上差分算法
int a,b;
cin>>a>>b;
s[a]++;
s[b]++;
s[LCA(a,b)]-=2;
}
/*解析:
因为要使得这m组组合不相通
所以先标记一下原来相通时状态,s[a]表示a到其父亲必须右边,而他们公共祖先往上不一定要边所以要减去2因为a,b各贡献+1
*/
//这个搜索是配合树上差分
void dfs2(int u,int Fa){//dfs2(当前节点下标,父节点下标)
for(int i=0;i<e[u].size();i++){//u的所有邻接点
int v=e[u][i],p=num[u][i];//v是此时与u相邻的点,p是u此时所在边的编号
if(v==Fa)continue;//不重复搜父亲
dfs2(v,u);
s[u]+=s[v];
if(s[v]==m)ans=max(ans,p);//p是关键边
}
}
//s[v]表示v到其父亲的这条路径在题目中作为关键路径所需要走的次数
例如上图,这两个点之间的有关键路径就对该路径+1,其实就是s[]变化!
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int>e[N];//vector数组的数组,其实就是邻接矩阵存边,例如e[u]表示与u相邻的点
vector<int>num[N];//边标号
int dep[N];//各结点深度
int fa[N][21];//注意他的第二位是向上2的多少次方级最多21表示2的21次方级,不能开N太大会爆!
int n,m;//n个顶点m种不相通组合
int ans=-1;//没有可行方案默认-1
int s[N];
void dfs(int u,int Fa){
dep[u]=dep[Fa]+1;
fa[u][0]=Fa;
//从下往上找
for(int i=1;i<=20;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(auto &v:e[u]){
if(v==Fa)continue;
dfs(v,u);
}
}
int LCA(int u,int v){//返回u,v的公共祖先下标
//保证u的深度更深
if(dep[u]<dep[v])swap(u,v);
//u大步向上跳
for(int i=20;i>=0;i--){
if(dep[fa[u][i]]>=dep[v]){
u=fa[u][i];
}
}
//如果u,v相遇
if(u==v){
return u;
}
//否则u,v一起大步向上跳
for(int i=20;i>=0;i--){
if(fa[u][i]!=fa[v][i]){
//u,v祖先不同就一起往上跳
u=fa[u][i];
v=fa[v][i];
}
}
return fa[u][0];//原u,v公共祖先是修改后u,v的公共父亲
}
//配合树上差分
void dfs2(int u,int Fa){//当前节点下标u,其父节点下标Fa
for(int i=0;i<e[u].size();i++){
int v=e[u][i];int p=num[u][i];
if(v==Fa)continue;
dfs2(v,u);
s[u]+=s[v];
if(s[v]==m){//所给这m种组合都要过。
ans=max(ans,p);
}
}
}
int main(){
cin>>n>>m;
//邻接表存边((n-1条))
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
e[x].push_back(y);
num[x].push_back(i);
e[y].push_back(x);
num[y].push_back(i);
}
//为LCA做准备
dfs(1,0);
//m种不相通组合,树上差分:
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
s[x]++;
s[y]++;
s[LCA(x,y)]-=2;
}
//砍树
dfs2(1,0);
cout<<ans;//输出答案
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】