2023第十四届蓝桥杯省赛C/C++大学A组题解

本文详细解析了10道编程题目,涉及幸运数计算、二进制判断、平方数识别、字符串操作、图论(颜色平衡树、买瓜、网络稳定性)、数学函数(异或和之和)、像素放置算法以及数论(质数与莫比乌斯函数)。展示了C++编程解决实际问题的方法和技巧。
摘要由CSDN通过智能技术生成

        每道题的详细题解已经单发,在此进行汇总。点击链接可以查看详细题解。

目录

第一题:幸运数

第二题:有奖问答

第三题:平方数

第四题:更小的数

第六题:买瓜

第七题:网络稳定性

第八题:异或和之和

第九题:像素放置

第十题:翻转硬币


第一题:幸运数

详细题解

满分代码(填空题为不超时代码)

#include <iostream>
using namespace std;
int a[100][10];
int b[100][10];
int main()
{
  for(int i=1;i<=9999;i++)
  {
    int temp=i,sum=0,cnt=0;
    while(temp)
    {
      cnt++;//计算位数
      sum+=temp%10;//计算数位和
      temp/=10;
    }
    a[sum][cnt]++;//前一半数字
    b[sum][cnt]++;//后一半数字
    for(int j=cnt+1;j<=4;j++) b[sum][j]++;//后一半数字首位可以为0
  }
  int ans=0;
  for(int i=1;i<=36;i++)
    for(int j=1;j<=4;j++)
      ans+=a[i][j]*b[i][j];
  cout<<ans;
  return 0;
}

第二题:有奖问答

详细题解

满分代码(填空题为不超时代码)

#include <iostream>
using namespace std;
int k = (1 << 10) - 1;
bool f(int x) {//判断二进制数是否包含连续10个1
    bool flag = 1;
    while (x) {
        if ((k & x) == k){
            flag = 0;
            break;
        }
        x = x >> 1;
    }
    return flag;
}
 
int main() {
    int ans = 0;
    for(int j=1;j<=22;j++)//当答题数大于8时,枚举前最多22位
    {
      if(j<=9){//前面答题少于10题时不可能连续答对10题,这些状态都满足要求
        ans+=(1<<j);
        continue;
      }
      for(int i=0;i<=(1<<j)-1;i++)
        if(f(i)) ans++;
    }
    cout << ans+2;//加上总共答7、8题时的两种情况
    return 0;
}

第三题:平方数

详细题解

满分代码:

#include <iostream>
using namespace std;
 
long long f(long long x) {return x/2+x/4;}
 
int main() {
    long long l, r;
    cin >> l >> r;
    cout <<f(r) - f(l - 1);
    return 0;
}

第四题:更小的数

详细题解

满分代码:

#include<bits/stdc++.h>
using namespace std;
int p[5005][5005];
int main()
{
  string str;
  cin>>str;
  int ans=0;
  for(int i=0;i<str.size();i++)
  {
    for(int j=i-1;j>=0;j--)
    {
      if(str[i]<str[j]){
        p[j][i]=1;
      }
      else if(str[i]==str[j]){
        p[j][i]=p[j+1][i-1];
      }
      ans+=p[j][i];
    }
  }
  cout<<ans;
  return 0;
}

第五题:颜色平衡树

第六题:买瓜

详细题解

满分代码:

#include <iostream>
#include<algorithm>
using namespace std;
double a[35];
double b[35];
int n,m;
int ans=35;
void f(int id,double sum,int cut)//当前选择第几个瓜,已拿重量,已劈次数
{
  if(sum==m)//拿够了
  {
    ans=min(ans,cut);
    return ;
  }
  if(id>n) return ;
  if(sum>m||cut>ans) return ;
  if(sum+b[n]-b[id-1]<m) return ;//剩下的瓜全拿,也不够目标重量
  f(id+1,sum,cut);//不拿这个瓜
  f(id+1,sum+a[id]/2,cut+1);//切一刀,拿一半
  f(id+1,sum+a[id],cut);//不切,全拿
}
int main()
{
  cin>>n>>m;
  for(int i=1;i<=n;i++)
    cin>>a[i];
  sort(a+1,a+n+1,greater<double>());//从大到小排序
  for(int i=1;i<=n;i++)
    b[i]=a[i]+b[i-1];//前缀和预处理
  f(1,0,0);
  if(ans<35) cout<<ans;
  else cout<<"-1";
  return 0;
}

第七题:网络稳定性

详细题解

满分代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
const int inf=1000010;
int n,m,q;
struct line{//存储边的结构体
	int x,y,val;
	line(int a,int b,int c){x=a,y=b,val=c;}
};
vector<line>link;//存储输入的所有边
int root[N];//并查集中代表某一元素根(祖先)的数组
bool vis[N];//标志是否遍历过
int dep[N];//深度
int dp[N][20];//动态规划存储第i个节点往前2^j个节点
int val[N][20];//动态规划存储第i个节点到往前2^j个节点中最小的边权值即稳定性
bool cmp(line a,line b) {return a.val>b.val;}
vector<pair<int,int>>node[N];//存储图的邻接链表
int find(int x)//并查集算法
{
	if(root[x]==0||root[x]==x) return root[x]=x;
	return root[x]=find(root[x]);
}
void fk()//Kruscal算法
{
	int cnt=0;
	for(int i=0;i<link.size();i++)//在主函数中先将边从大到小排序
	{
		int x=link[i].x,y=link[i].y;
		if(find(x)==find(y)) continue;//边的两点没有连通的话才加入这条边
		node[x].push_back(make_pair(y,link[i].val));//将点y加入点x的链表
		node[y].push_back(make_pair(x,link[i].val));//将点x加入点y的链表
		cnt++;//已加入生成树中边的总数
		x=find(x),y=find(y);
		root[y]=x;//将x,y两点连通
		if(cnt>=n-1) break;//边数超过n-1,生成树已建成
	}
}
void dfs(int x)//对x节点进行遍历
{
	for(int i=1;(1<<i)<dep[x];i++)//当2^i大于x的深度时结束状态转移
	{
		dp[x][i]=dp[dp[x][i-1]][i-1];//等于往前2^(i-1)个节点的节点往前2^(i-1)个节点
		val[x][i]=min(val[x][i-1],val[dp[x][i-1]][i-1]);
	}
	for(int i=0;i<node[x].size();i++)//遍历所有子节点
	{
		int t=node[x][i].first;
		if(vis[t]) continue;
		vis[t]=1;//标志为已遍历
		dep[t]=dep[x]+1;//子节点深度+1
		dp[t][0]=x;//子节点的父节点表示为x
		val[t][0]=node[x][i].second;//子节点往前1个节点中最小边权值为与父节点相连的边权值
		dfs(t);//遍历子节点
	}
}
int lca(int x,int y)//求x和y的最近公共祖先lca
{
	int ans=inf;//设置稳定性ans的初始值	
	if(dep[x]<dep[y]) swap(x,y);//令x节点为较深的节点
	int k=log2(dep[x]-dep[y]);//移动步幅的最大值
	for(int i=k;i>=0;i--)//将x向上移动
	{
		if(dep[dp[x][i]]>=dep[y])//x移动2^j步后深度仍然不浅与y
		{
			ans=min(ans,val[x][i]);
			x=dp[x][i];
		}
	}//移动后x与y深度相同
	if(x==y) return ans;//y是x的祖先
	k=log2(dep[x]);//移动步幅的最大值
	for(int i=k;i>=0;i--)
	{
		if(dp[x][i]==dp[y][i]) continue;//移动2^i步后是x和y的祖先,跳过
		ans=min(ans,val[x][i]);
		ans=min(ans,val[y][i]);//移动2^i步后更新稳定性
		x=dp[x][i];
		y=dp[y][i];//移动x和y
	}//移动后x和y的上一个节点是lca
	ans=min(ans,val[x][0]);
	ans=min(ans,val[y][0]);//比较最后两条边,x和y到lca的边权值
	return ans;
}
int main()
{
	cin>>n>>m>>q;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		link.push_back(line(x,y,z));
	}
	sort(link.begin(),link.end(),cmp);//从大到小排序所有边
	fk();//kruscal建立最大生成树
	for(int i=1;i<=n;i++)
	{
		if(vis[i]) continue;
		vis[i]=1;
		dep[i]=1;//根节点深度为1
		dfs(i);//动态规划求lca
	}
	for(int i=1;i<=q;i++)
	{
		int x,y;
		cin>>x>>y;
		if(find(x)!=find(y)) cout<<"-1"<<endl;//并查集检查是否两点是否连通
		else cout<<lca(x,y)<<endl;//lca算法求稳定性
	}
	return 0;
}

第八题:异或和之和

详细题解

满分代码:

#include <iostream>
using namespace std;
long long a[200005];
long long sum[200005];
int main() {
  int n;
  cin >> n;
  for(int i=1;i<=n;i++)
    cin>>a[i];
  for(int i=1;i<=n;i++)
    sum[i]=sum[i-1]^a[i];
  long long ans = 0;
  for(int k=0;k<=20;k++)//枚举每一对异或和的二进制第k位
  {
    for(int i=1;i<=n;i++)
    {
      for(int j=i;j<=n;j++)
      {
        long long temp=((sum[j]^sum[i-1])>>k)&1;//位运算取1个数字的第k位数字
        ans+=temp*(1<<k);//结果加上这一对异或和的第k位乘以2^k
      }
    }
  }
  cout << ans;
  return 0;
}

第九题:像素放置

详细题解

满分代码:

#include<bits/stdc++.h>
using namespace std;
int Map[15][15];//方格的颜色
vector<int> limit[15][15];//方格影响到的数字方格
struct num_p{//表示数字方格的结构体
  int sum;//周围九宫格的黑色总数
  int x,y;//数字方格的坐标
  int lastX,lastY;//该数字方格影响的最后一个方格坐标
  num_p(int a,int b,int c){sum=a;x=b;y=c;}
};
vector<num_p> nums;//存放所有数字方格的数组
int n,m;
bool ans=0;//是否找到答案
bool islast(int id,int x,int y)//判断某一坐标是不是数字方格影响的最后一个方格
{
    return (x==nums[id].lastX&&y==nums[id].lastY);
}
void dfs(int x,int y)//将编号为x的方格涂成y颜色
{
    if(ans) return ;//已经找到答案 不再继续执行
    int tx=x/m,ty=x%m;//将编号转为坐标
    Map[tx][ty]=y;//将方格涂色
    if(y)
    {
        for(auto it=limit[tx][ty].begin();it!=limit[tx][ty].end();it++)
            nums[*it].sum--;//如果涂成黑色,将影响到的所有数字减一
    }
    for(auto it=limit[tx][ty].begin();it!=limit[tx][ty].end();it++)
    {
        if(nums[*it].sum<0) return ;//有数字被减到小于0
        if(islast(*it,tx,ty)&&nums[*it].sum>0) return ;//有数字没有被减到0
    }
    if(x>=n*m-1)//枚举到了最后一个
    {
        ans=1;
        return ;
    }
    dfs(x+1,0);//将下一号涂成白色
    dfs(x+1,1);//将下一号涂成黑色
    tx=(x+1)/m,ty=(x+1)%m;
    for(auto it=limit[tx][ty].begin();it!=limit[tx][ty].end();it++)
        nums[*it].sum++;//撤销将下一号涂成黑色产生的影响
}
int main()
{
  cin>>n>>m;
  char c;
  for(int i=0;i<n;i++)
  {
    for(int j=0;j<m;j++)
    {
      cin>>c;
      if(c<='9'&&c>='0') nums.push_back(num_p(c-'0',i,j));//将数字方格存起来
    }
  }
  for(auto it=nums.begin();it!=nums.end();it++)
  {
    for(int i=it->x-1;i<=it->x+1;i++)
    {
      for(int j=it->y-1;j<=it->y+1;j++)
      {
        if(i<0||i>=n||j<0||j>=m) continue;
        it->lastX=i,it->lastY=j;//找到数字方格影响的最后一个方格
        limit[i][j].push_back(int(it-nums.begin()));//坐标i,j受到本数字方格影响
      }
    }
  }
  dfs(0,0);//将0号方格涂成白色
  dfs(0,1);//将0号方格涂成黑色
  for(int i=0;i<n;i++)
  {
      for(int j=0;j<m;j++)
      cout<<Map[i][j];
      cout<<endl;
  }
  return 0;
}

第十题:翻转硬币

2023第14届蓝桥杯大赛软件赛省赛C/C++大学A组第10题题解:翻转硬币-CSDN博客

满分代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e7;
long long ans=0,n;
bool vis[N];
vector<int> prime;//存储质数的容器
int f_mu[N],sum_mu[N];//莫比乌斯函数的值,莫比乌斯函数的前缀和
unordered_map<int,int> sum_mu_long;//莫比乌斯函数的前缀和,当n超过数组的存储范围时用map存储
void findprime()//寻找质数,主要是计算莫比乌斯函数的值和前缀和
{
    f_mu[1]=1;
    sum_mu[1]=1;//设定u(1)的初始值和前缀和
    for(int i=2;i<=n&&i<N;i++)
    {
        if(vis[i]==0)//未被筛过
        {
            prime.push_back(i);//存入质数
            f_mu[i]=-1;//质数的莫比乌斯函数为1
        }
        for(int j=0;j<prime.size()&&i*prime[j]<=n&&i*prime[j]<N;j++)//遍历质数
        {
            vis[i*prime[j]]=1;//标记为合数
            if(i%prime[j]==0)
            {
                f_mu[i*prime[j]]=0;//包含平方因子
                break;
            }
            f_mu[i*prime[j]]=-f_mu[i];//不包含平方因子,质因子个数奇偶性改变
        }
        sum_mu[i]=f_mu[i]+sum_mu[i-1];//前缀和递推
    }
}
int SumMu(int x)//求莫比乌斯函数的前缀和
{
    if(x<N) return sum_mu[x];
    if(sum_mu_long.count(x)) return sum_mu_long[x];
    int sum=1;
    for(long long l=2,r;l<=x;l=r+1)
    {
        r=x/(x/l);//整除分块
        sum-=SumMu(x/l)*(r-l+1);//杜教筛的递推公式
    }
    return sum_mu_long[x]=sum;
}
int main()
{
    cin>>n;
    findprime();
    for(long long l=1,r;l*l<=n;l=r+1)
    {
        r=sqrt(n/(n/(l*l)));//整除分块
        ans+=(SumMu(r)-SumMu(l-1))*(n/(l*l));
    }
    cout<<ans;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值