【蓝桥杯】2023年十四届省赛大学B组真题(10题)(c、c++) 1.日期统计2.01串的熵3.冶炼金属 4.飞机降落5.接龙数列6.岛屿个数7.子串简写8.整数删除9.景区导游 10. 砍树

博客汇总了蓝桥杯的10道题目,包括日期统计、01串的熵等。对各题给出了问题描述、答案提交要求及题解,部分题解是自己的,部分是他人的,还给出了如岛屿个数、砍树等题的解题思路,涉及暴力、前缀和、优先队列等方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目: 1. 日期统计

问题描述

小蓝现在有一个长度为 100的数组,数组中的每个元素的值都在 0 到 9 的范围之内。数组中的元素从左至右如下所示:

5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2
7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1
0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3

现在他想要从这个数组中寻找一些满足以下条件的子序列:

  1. 子序列的长度为 8;
  2. 这个子序列可以按照下标顺序组成一个 yyyymmdd 格式的日期,并且要求这个日期是 2023年中的某一天的日期,例如 20230902,20231223。yyyy 表示年份,mm 表示月份,dd 表示天数,当月份或者天数的长度只有一位时需要一个前导零补充。

请你帮小蓝计算下按上述条件一共能找到多少个不同的 2023 年的日期。对于相同的日期你只需要统计一次即可。

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

题解:

#include <bits/stdc++.h>
using namespace std;
int main() {
    int array[100] = {
        5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7,
        5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9,
        2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3,
        8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6,
        1, 4, 0, 1, 0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3
    };

    int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int ans = 0;

    for (int month = 1; month <= 12; ++month) {
        for (int day = 1; day <= daysInMonth[month]; ++day) {
            int dateSeq[8] = {2, 0, 2, 3, month / 10, month % 10, day / 10, day % 10};
            int k = 0;

            for (int i = 0; i < 100; ++i) {
                if (array[i] == dateSeq[k]) {
                    ++k;
                    if (k == 8) {
                        ans++;
                        break;
                    }
                }
            }
        }
    }

    printf("%d\n", ans);
    return 0;
}

题目:2. 01串的熵

题解:

//首先要理解题目的意思
//不要被题目的数据吓到
//例如当S等于100时,
100的信息熵 =
 -0的个数*(0的个数/总位数)*log2(0的个数/总位数)-1的个数*(1的个数/总位数)*log2(1的个数/总位数)
//然后我们  长度为23333333的01串 从0的个数为0开始枚举,直到1.0*23333333/2
(因为0的个数比1的个数少,所以一定不会超过23333333的一半)
//注意点:在判断浮点数是否等于一个数的时候不能if(x == y)  
而是要判断它是否属于某一范围,或者二者差的绝对值属于某一范围一般取<0.01
#include<stdio.h>
#include<math.h>
int main(){
    double n = 23333333,sum = 0;
    int o = 0,l = 0;
    for(o = 0;o <= n/2;o++){
        sum = 0;
        sum -= o*(o / n) * log2(o / n) + (n - o)*((n - o) / n) * log2((n - o) / n);
        if(sum > 11625907.5 && sum < 11625907.6){
            printf("%d",o);
            break;
        }
    }
    return 0;
}

题目:3. 冶炼金属

题解:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
  int n,a,b;
  int max=INT_MAX,min=0;
  scanf("%d",&n);
  for(int i=0;i<n;i++){
    scanf("%d %d",&a,&b);
    if(max>a/b){
      max=a/b;
    }
    if(min<a/(b+1)+1){
      min=a/(b+1)+1;
    }
  }
 printf("%d %d",min,max);
  return 0;
}

题目:4. 飞机降落

题解:

#include <iostream>
#include <vector>
using namespace std;

// 创建飞机结构体变量
struct plane
{
    int t, d, l;
};
bool vis[15];  // true表示飞机降落,false表示飞机未降落
bool flag;  // 标记是否全部安全降落
vector<plane> p(15);
// 深搜
void dfs(int m, int cnt, int last)  // last表示此前所有飞机降落所需的单位时间
{
    if (cnt == m)
    {
        flag = true;
        return;
    }
    for (int i = 0; i < m; i++)
    {
        if (!vis[i] && p[i].t + p[i].d >= last)  // 只有来的时刻+盘旋时间 > last 的飞机才可以安全降落
        {
            vis[i] = true;
            dfs(m, cnt + 1, max(last, p[i].t) + p[i].l);
            vis[i] = false;
        }
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int N;
        cin >> N;
        for (int i = 0; i < N; ++i)
            cin >> p[i].t >> p[i].d >> p[i].l;
        flag = false;
        dfs(N, 0, 0);
        if (flag)
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
    return 0;
}

题目:5. 接龙数列

题解:

#include <iostream>
#include <string>
using namespace std;

int dp[10];

int main()
{
  int n;
  cin>>n;
  string s;
  int m=0;
  for(int i=0;i<n;++i){
    cin>>s;
    int x=s[0]-'0',y=s[s.size()-1]-'0';
    dp[y]=max(dp[x]+1,dp[y]);
    m=max(m,dp[y]);
  }
  cout<<n-m<<endl;
  return 0;
}

题目:6. 岛屿个数

题解:(没写出来,题解是别人的)

解题思路

首先明白用dfs搜索出一块岛屿,那么要解决岛屿是否是子岛的问题。由题意可知,子岛是在内海中的,题目需要查找的岛屿是在外海中的,并且外海是相连通的,可以用dfs搜索全部外海。从外海开始遍历岛屿,这样就遍历不到子岛

#include <stdio.h>
#include <stdlib.h>

int M, N, d[52][52];

void dfs_sea(int i, int j)
{
    if ((i >= 0 && i <= M + 1) && (j >= 0 && j <= N + 1))
    {
        if (d[i][j] == 0)
        {
            d[i][j] = 2;//标记出外海
            //八个方向 
            dfs_sea(i, j + 1);
            dfs_sea(i, j - 1);
            dfs_sea(i + 1, j);
            dfs_sea(i + 1, j + 1);
            dfs_sea(i + 1, j - 1);
            dfs_sea(i - 1, j);
            dfs_sea(i - 1, j + 1);
            dfs_sea(i - 1, j - 1);
        }
    }
}

void dfs_island(int i, int j)
{
    if ((i >= 0 && i <= M + 1) && (j >= 0 && j <= N + 1))
    {
        if (d[i][j] == 1)
        {
            d[i][j] = 3;//搜索过的岛屿不再搜索 
            dfs_island(i + 1, j);//右 
            dfs_island(i - 1, j);//左 
            dfs_island(i, j + 1);//上 
            dfs_island(i, j - 1);//下 
        }
    }
}

int main(int argc, char *argv[])
{
    // 请在此输入您的代码
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d %d", &M, &N);
        //填充海水
        for (int i = 0; i < N + 2; i++)
        {
            d[0][i] = 0;
            d[M + 1][i] = 0;
        }
        for (int i = 1; i < M + 1; i++)
        {
            d[i][0] = 0;
            d[i][N + 1] = 0;
        }
        //输入图 
        for (int i = 1; i < M + 1; i++)
        {
            for (int j = 1; j < N + 1; j++)
            {
                scanf("%1d", &d[i][j]);
            }
        }

        dfs_sea(0, 0); //找出所有外海 

        int count;//计算岛屿数量 
        count = 0;
        for (int i = 0; i < M + 2; i++)
        {
            for (int j = 0; j < N + 2; j++)
            {
                if (d[i][j] == 1 && d[i - 1][j] == 2)//只能从外海搜索岛屿,所以岛屿前一定是外海“2”
                {
                    dfs_island(i, j);//搜索岛屿 
                    count++;
                }
            }
        }
        printf("%d\n", count);
    //以下代码可以打印出处理后的图
        /*for (int i = 0; i < M + 2; i++)
        {
            for (int j = 0; j < N + 2; j++)
            {
                printf("%1d", d[i][j]);
                if (j == N + 1)
                printf("\n");
            }
        }*/
    }
    return 0;
}

题目:7. 子串简写

题解:

(70%,剩下百分之30运行超时了,暴力)

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main()
{
  int k,ans=0;
  char s[500050];
  char c1,c2;
  scanf("%d",&k);
  scanf("%s %c %c",&s,&c1,&c2);
  for(int i=0;i<strlen(s);i++){
    if(s[i]==c1){
      for(int j=i+k-1;j<strlen(s);j++){
        if(s[j]==c2){
          ans++;
        }
      }
    }
  }
  printf("%d",ans);
  return 0;
}

(全对的,前缀和)

#include<bits/stdc++.h>
using namespace std;
int n,l,c[500005];
long long sum;
char s[500005],a,b;
int main(){
  scanf ("%d\n%s %c %c",&n,s+1,&a,&b);
  l=strlen(s+1);
  for(int i=1;i<=l;i++){
    if (s[i]==a) c[i]++;
    c[i]+=c[i-1];
    if (i>=n&&s[i]==b){
      sum+=c[i-n+1];
    }
  }
  cout<<sum;
  return 0;
}

题目:8. 整数删除

题解:

(只对百分之30,纯暴力,其他会超时)

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main()
{
  int n,k;
  scanf("%d %d",&n,&k);
  int a[n];
  for(int i=0;i<n;i++){
    scanf("%d",&a[i]);
  }
  int k1=k,n1=n;
  while(k1--){
    int min=0;
    for(int i=0;i<n1;i++){
      if(a[min]>a[i]){
        min=i;
      }
    }
    if(min==0){
      a[min+1]=a[min+1]+a[min];
      for(int i=min;i<n;i++){
        a[i]=a[i+1];
      }
    }else{
      a[min-1]=a[min-1]+a[min];
      a[min+1]=a[min+1]+a[min];
      for(int i=min;i<n1-1;i++){
        a[i]=a[i+1];
      }
    }
    n1--;
  }
  for(int i=0;i<n-k;i++){
    if(i!=n-k-1){
      printf("%d ",a[i]);
    }else{
      printf("%d",a[i]);
    }
  }
  return 0;
}

(全对的,优先队列和双向列表,题解是别人的)

#include <iostream>
using namespace std;
#include<vector>
#include<queue>
#define int long long 
const int N = 5e5 + 10;
int n, k;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> que;//优先队列维护最小值
int pre[N], ne[N];//维护左边元素和右边元素的下标的下标
int cnt[N], a[N], tmp;//cnt代表下标为i的元素需要修改的值
signed main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> tmp;
        que.push(make_pair(tmp, i));
        pre[i] = i - 1;//下标为i的元素的左边元素的下标为i-1
        ne[i] = i + 1;//下标为i的元素的右边的元素的下标为i+1
    }
    while (que.size() > n - k) {//查找k次
        int num = que.top().first;//获取最小值
        int id = que.top().second;//获取最小值的下标
        que.pop();
        /*这里cnt非0,说明在前面的操作过程中,该元素已经进行修改了,但是队列中还没有更新
        现在对队列的这个值进行修正,修正后重新查找最小值*/
        if (cnt[id]) {
            que.push({ num + cnt[id],id });
            cnt[id] = 0;
        }
        else {
            int left = pre[id];
            int right = ne[id];
            cnt[left] += num;//对左边的值进行修改
            cnt[right] += num;//对右边的值进行修改
            //将该元素在双向链表中删除
            ne[left] = right;
            pre[right] = left;
        }
    }
    while (!que.empty()) {
        int num = que.top().first;
        int id = que.top().second;
        que.pop();
        a[id] = num + cnt[id];
    }
    for (int i = 1; i <= n; i++) {
        if (a[i]) {
            cout << a[i] << " ";
        }
    }
    return 0;
}

题目:9. 景区导游

题解:(没写出来,这是别人的题解)

//本题主要考察LCA
//若要求u->v的路径长度,等于求u->root+v->root - root->2*LCA(u,v)
//即分别从u和v走到根结点,再减去2倍的根结点到(u,v)的最近公共祖先的路径长度
//同理,若原本的遍历顺序是a->b->c,总路径长度为sum,若要跳过b,则新的路径长度为
//sum-dist(a,b)-dist(b,c)+dist(a,c),可画图验证 
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N=1e5+100;

int n,k;
int deep[N];//深度 
int dp[N][21];//dp[i][j]表示从i结点开始跳2^j步可到达的结点 
vector<int>edge[N];//边 
vector<int>weight[N];//权值 
ll path[N];//原始的游览路线 
ll dist[N];//dist[i]存储i到根结点的距离 

void dfs(int u,int father)//LCA的前置算法(模板) 
{
    deep[u]=deep[father]+1;
    dp[u][0]=father;
    for(int i=1;i<=20;i++)
    {
        dp[u][i]=dp[dp[u][i-1]][i-1];    
    }    
    
    for(int i=0;i<edge[u].size();i++)
    {
        int v=edge[u][i],w=weight[u][i];
        if(v==father)continue;
        dist[v]=dist[u]+w;
        dfs(v,u);    
    }
}

int LCA(int x,int y)//求x和y的最近公共祖先(模板) 
{
    if(deep[x]<deep[y])swap(x,y);
    for(int i=20;i>=0;i--)
    {
        if(deep[dp[x][i]]>=deep[y])
        {
            x=dp[x][i];
        }
    }
    if(x==y)return x;
    for(int i=20;i>=0;i--)
    {
        if(dp[x][i]!=dp[y][i])
        {
            x=dp[x][i];
            y=dp[y][i];
        }
    }
    return dp[x][0];
}

ll get_dist(int x,int y)//求x和y的距离 
{
    if(x==0||y==0)return 0;
    return dist[x]+dist[y]-2*dist[LCA(x,y)];     
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<n;i++)//插入n-1条无向边 
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        edge[u].push_back(v);
        edge[v].push_back(u);
        weight[u].push_back(w);
        weight[v].push_back(w);    
    }    
    dfs(1,0);//跑一遍dfs为LCA做准备 
    ll sum=0;//sum存储原始游览路线的总路径长度 
    for(int i=1;i<=k;i++)
    {
        scanf("%d",&path[i]);
        sum+=get_dist(path[i],path[i-1]);//依次累加 
    }
    
    for(int i=1;i<=k;i++)//除去第i个景点 
    {
        ll dist1=get_dist(path[i],path[i-1]);
        ll dist2=get_dist(path[i],path[i+1]);
        ll dist3=get_dist(path[i-1],path[i+1]);
        printf("%lld ",sum-dist1-dist2+dist3);//套公式计算即可 
    }
    printf("\n");
    return 0;
}

题目:10. 砍树

题解: (没写出来,题解是别人的)

解题思路

1、理解树上点差分 2、理解树上边差分(本题的) 3、题目说要砍掉边,换句话说,答案这条边是这些路径的公共边,走过了m次。(不会树上差分就dfs,对当前道路走过的次数进行回溯) 4、最后dfs1可以O(n)的求出每条路径走过多少遍 cnt[i]表示编号为i的边走过多少次,l[i]表示标号为i的点对应的边

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

using ll=long long;
const int N=1e5+9;
ll n,m,dep[N],fa[N][24],cnt[N],l[N],ans=-1;
struct node
{
    int point,s;
};
vector<node>g[N];

void dfs(int x,int p)
{
    dep[x]=dep[p]+1;
    fa[x][0]=p;
    for(int i=1;i<=20;i++)
    fa[x][i]=fa[fa[x][i-1]][i-1];
    for(const auto &y:g[x])
    {
        if(y.point==p)continue;
        l[y.point]=y.s;
        dfs(y.point,x); 
    }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)
    if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)
    if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

void dfs1(int x)
{
    for(const auto &i:g[x])
    {
        if(i.point==fa[x][0])continue;
        dfs1(i.point);
        cnt[l[x]]+=cnt[l[i.point]];
    }
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<n;i++)
    {
        int u,v;cin>>u>>v;
        g[u].push_back({v,i}),g[v].push_back({u,i});
    }
    dfs(1,0);
    for(int i=1;i<=m;i++)
    {
        int u,v;cin>>u>>v;
        int p=lca(u,v);
        cnt[l[u]]++,cnt[l[v]]++,cnt[l[p]]-=2;
    }
    dfs1(1);
    //for(int i=1;i<=n;i++)cout<<cnt[i]<<' ';cout<<'\n';
    for(int i=1;i<=n;i++)
    if(cnt[i]==m)ans=i;
    cout<<ans<<'\n';
    return 0;
}
/*
7 3
1 2
1 3
2 4
2 5
3 6
3 7

4 7
3 5
6 1
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值