【蓝桥杯】第十三届蓝桥杯C/C++省赛 B组题解

本文介绍了作者在蓝桥杯编程竞赛中的解题思路,包括九进制转十进制、日期顺子判断、刷题统计、灌木修剪问题、X进制减法、子矩阵和统计、积木填充计数、扫雷问题以及李白打酒问题的解决方案,并提供了相应的C++代码实现。文章详细分析了每道题目的关键点和算法思想。

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

前言:

因为考完蓝桥杯后在疯狂的补作业,所以没来得及更新hhh,现在就来更新一下当时在比赛场上的思路和理解吧,欢迎各位大佬批评指正!

试题 A: 九进制转十进制

在这里插入图片描述
第 i 位的权值是9^(i-1),然后就是每位上的数乘以这位数的权值加和即可,就是2 + 29 + 099 + 2999 = 1478
所以说答案为 1478

试题 B: 顺子日期

在这里插入图片描述
这个题就有很多歧义了,有的人说是4,有的人说是14,我当时写的是14,那么我阐述一下我的理由吧,因为他说了是在日期的yyyymmdd表示法中的,所以说前导零是存在的,他这个顺子日期是指的连续的三位数,那么0一定是数字呀,那么就写个程序就跑出来啦,当然我也是最后检查的时候把365个日期都输出出来一个接一个的数的qaq,确实是14个,那么就附上我当时写的代码吧:

#include<bits/stdc++.h>
using namespace std;
int cnt[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

int main(){
	string s = "20220";
	int ans = 0;
	for(int i=1;i<=9;i++){
		for(int j=1;j<=cnt[i];j++){
			string t = s + (char)(i + '0'),a;
			int x = j;
			while(x){
				a += x%10+'0';
				x /= 10;
			}
			if(a.size() == 1) a += '0';
			reverse(a.begin(),a.end());
			t += a;
			for(int i=0;i+2<t.size();i++){
				if(t[i] == t[i+1]-1 && t[i+1] == t[i+2]-1){
					ans++;
					cout<<t<<endl; 
					break;
				}
			}
		}
	}
	s.pop_back();
	for(int i=10;i<13;i++){
		for(int j=1;j<=cnt[i];j++){
			int x = i;
			string a,t;
			while(x) a += x%10+'0',x /= 10;
			if(a.size() == 1) a+='0';
			reverse(a.begin(),a.end());
			t = s + a;
			a.clear();
			x = j; 
			while(x) a += x%10+'0',x /= 10;
			if(a.size() == 1) a+='0';
			reverse(a.begin(),a.end());
			t += a;
			for(int i=0;i+2<t.size();i++){
				if(t[i] == t[i+1]-1 && t[i+1] == t[i+2]-1){
					ans++;	
					cout<<t<<endl;
					break;
				}
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

试题 C: 刷题统计

在这里插入图片描述

题意:

这个题就是跟你说一周中周一到周五每天都做a道题,周六周天每天做b道题,给你一个数n,问你第几天才能做到大于等于n道题

思路:

每周做5a + 2b道题,这个地方有个细节,就是题目中说了a和b是小于等于1e18的,那么5a + 2b会不会爆long long呢,然后我当时就打开了python算了一下发现long long是9e18,刚好没爆,那么就是可以的,就是首先看n道题做多少完整的周,剩下的从第一天到第7天暴力就行,时间复杂度为O(1),那么下面来看代码吧:

#include<iostream>
using namespace std;
typedef long long LL;

int main(){
	LL a,b,n;
	cin>>a>>b>>n;
	LL t = a*5+b*2;
	LL ans = n/t*7;//ans代表的是答案,这里求的是整的周数,一周七天然后乘以7
	n %= t;
	for(int i=1;i<=5;i++){
		if(n <= 0) break;
		n -= a;
		ans++;
	}
	for(int i=1;i<=2;i++){
		if(n <= 0) break;
		n -= b;
		ans++; 
	}
	cout<<ans<<endl;
	return 0;
}

试题 D: 修剪灌木

在这里插入图片描述

题意:

有一排树,标号为1~n,初始长度为1,每天从早晨到傍晚都会增长1,这个人从1号树开始修剪,每天都修剪一棵树然后下一天修建下一棵树,当修建到头了然后就回头再重复这些步骤,问你每棵树最高能长到多么高。

分析:

纯思维题:看样例,第一天当这个人修建第一棵树的时候第一棵树已经长到1了,然后第二天修建第二棵树的时候这棵树已经长到2了,因为这个人是不断的进行重复的修建的,当这个人再一次修建到第一棵树的时候这棵树已经长了4天了,那么这个4是怎么来的呢?这不就是(n-1)*2吗,就是这个人修剪右边树的数量乘以二,那么第3棵树的最大高度是多少呢,就是它左边树的高度乘以二,其实这个答案是对称的,因为这个人修剪的是循环往复的,只需要对每棵树输出它左边树的数量和右边树的数量的最大值乘以二就行,复杂度O(N),那么现在看一下代码吧:

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

int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int ans = max((i-1)*2,(n-i)*2);
		cout<<ans<<endl;//因为题目中说了最大是1e4,那么用cout应该也不会超时的
	}
	return 0;            	
}

试题 E: X 进制减法

在这里插入图片描述
在这里插入图片描述

题意:

给你一个数A和一个数B,题目中要求了A>=B,A和B都是相同的X进制,这个题目中的X进制就是每个位上的进制都是2~N上的任意数,问你A-B的最小可能的十进制数是多少

分析:

咱们先看一下这个X进制是什么意思吧,这个题目中给出来了一个例子,就是最低数位为二进制,第二数位为十进制,第三数位为八进制,则X 进制数 321 转换为十进制数为 65,这个65是怎么出来的呢?第一位数是1,这个毋庸置疑,因为最低数位是二进制,所以逢2进1,第二位是2,那么他就进位了两次,就是2*2=4,第二位是10进制,那么这个第三位是3,就进了3次位,就相当于是第二位的30,相当于第一位的60,那么答案就是60+4+1 = 65啦,直接上推出来的结论吧,第i位的权重就是1~i-1位的进制乘积,那么咱们有了这个前提就明白了,只要是使得A和B的进制的每一位的进制都尽可能小就行了,那么就是假如说A这个数的第 i 位的数字是A[i]吧,B是B[i],那么第i位的进制的最小值就是max(A[i],B[i])+1,因为题目中说了下限是2,那么第 i 位用贪心的思想的进制就是max(2,max(A[i],B[i])+1),那么进制求出来了,每个位数按照小学的方法减一下最后就能求出答案啦,下面看一下代码吧:

#include<bits/stdc++.h>
using namespace std;
const int N = 100010,mod = 1000000007;
typedef long long LL;
LL a[N],b[N],cnt1,cnt2,ans[N],cnt,c[N];
int main(){
	int n;
	cin>>n;
	cin>>cnt1;
	for(int i=cnt1;i>=1;i--) scanf("%lld",&a[i]);
	cin>>cnt2;
	for(int i=cnt2;i>=1;i--) scanf("%lld",&b[i]);
	int op = 0;//这个是借位,就是向高位的借位
	for(int i=1;i<=cnt1;i++){
		int t = a[i] - b[i] + op;
		if(t < 0) op = -1;
		else op = 0;
		c[i] = max(2ll,max(a[i],b[i])+1);
		ans[i] = (t + c[i]) % c[i];//这里可能减出来的是负数,咱们需要加一下这一位的进制再%一这一位的进制
	}
	LL res = 0,jin = 1;
	for(int i=1;i<=cnt1;i++){
		res += ans[i] * jin % mod;
		res %= mod;
		jin *= c[i];
		jin %= mod;
	}
	cout<<res<<endl;
	return 0;
}

试题 F: 统计子矩阵

在这里插入图片描述
在这里插入图片描述

题意:

给你一个N*M的矩阵,每个点都有个数,问你有多少个子矩阵的和小于等于K

分析:

当时在场上没想到正解,就直接写了个二位前缀和莽的,赛后听了各位学长的思路发现可以枚举l和r然后进行双指针扫一扫就能O(N^3)直接就能过了,我太菜了wow,下面就附上我的暴力代码吧,%70的数据我的那个N的四次方的复杂度应该是跑1e8次,咱也不知道蓝桥杯的评测姬能不能跑出来,正解有空再整理,先看一下那个暴力的吧!

#include<iostream>
using namespace std;
typedef long long LL;
const int N = 510;
LL g[N][N],f[N][N];
LL k;
int main(){
	int n,m;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>g[i][j];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			f[i][j] = f[i-1][j] + f[i][j-1] - f[i-1][j-1] + g[i][j];
		}
	}
	LL ans = 0;
	for(int x1=1;x1<=n;x1++){
		for(int y1=1;y1<=m;y1++){
			for(int x2=x1;x2<=n;x2++){
				for(int y2=y1;y2<=m;y2++){
					LL t = f[x2][y2] - f[x2][y1-1] - f[x1-1][y2] + f[x1-1][y1-1];
					if(t <= k){
						ans++;
					}
				}
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

正解代码:

待整理…

试题 G: 积木画

在这里插入图片描述
在这里插入图片描述

题意:

给你两种积木,然后给你一个2*N的方框,问用这两种积木填满这个方框有多少种方法,注意这俩积木可以各种旋转。

分析:

这个题我一看到就想到了Acwing那个蒙德里安的梦想那个题了,点此查看那个题,这个题就是那个题的降级版,是一个裸的状压dp,f[i][j]表示前i-1列都填满,第i列的状态为j的方案数,这个状态一共就4种,就是0,1,2,3,都是化成二进制来看的,一共两位,哪一位是1就代表哪一位上填起来,还有就是注意一下MLE的问题,这个题可能会MLE,就是1e7的数据量的话只能开int,long long就暴了,滚动数组优化的话那就没关系啦,下面请看代码:

#include<iostream>
using namespace std;
const int N = 1e7+3,mod = 1000000007;
int f[N][4];

int main(){
	int n;
	cin>>n;
	f[0][3] = 1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<4;j++){
			if(j == 0){
				f[i][j] = f[i-1][3];
			}
			else if(j == 1){
				f[i][j] = (f[i-1][0] + f[i-1][2]) % mod;
			}
			else if(j == 2){
				f[i][j] = (f[i-1][0] + f[i-1][1]) % mod;
			}
			else if(j == 3){
				f[i][j] = ((f[i-1][1] + f[i-1][2] ) % mod + (f[i-1][3] + f[i-1][0]) % mod) % mod;
				//注意这个地方因为是四个数加起来,所以说要两两取模,不然会爆int
			}
		}
	}
	cout<<f[n][3]<<endl;
	return 0;
}

试题 H: 扫雷

在这里插入图片描述
在这里插入图片描述

题意:

就是有地雷,第i个地雷是在xi,yi那个地方埋着,爆炸的范围是ri,也有排雷导弹,同样是这样的形式,排雷导弹范围内的地雷会爆炸,每个地雷爆炸范围内的地雷也会爆炸,那么问发射m颗排雷导弹后能有多少地雷爆炸。

分析:

当时是直接建边dfs暴力的40%的测试样例,那么现在来看一下代码吧:

#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
const int N = 100010, M = N+N;
struct node{
	int x,y,r;
};
node g[N];
bool st[N];
int h[N],e[M],ne[M],idx;
int n,m;
void add(int a,int b){
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
};
int ans;
void dfs(int u){
	if(st[u]) return;
	st[u] = true;
	ans++;
	for(int i=h[u];i!=-1;i=ne[i]){
		int j = e[i];
		dfs(j);
	}
}
double dis(int i,int j){
	return sqrt((g[i].x-g[j].x)*(g[i].x-g[j].x) + (g[i].y-g[j].y)*(g[i].y-g[j].y));
}
bool check(int i,int j){
	double d = dis(i,j);
	return d < double(g[i].r) || d < double(g[j].r);
}
int main(){
	cin>>n>>m;
	memset(h,-1,sizeof h);
	for(int i=1;i<=n;i++) cin>>g[i].x>>g[i].y>>g[i].r;
	for(int i=1;i<=m;i++) cin>>g[i+n].x>>g[i+n].y>>g[i+n].r;
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(check(i,j)) add(i,j),add(j,i);
		}
	}
	for(int i=n+1;i<=n+m;i++){
		for(int j=1;j<=n;j++)
			if(check(i,j)) dfs(j);
	}
	cout<<ans<<endl; 
	return 0;
}

试题 I: 李白打酒加强版

在这里插入图片描述
在这里插入图片描述

题意:

李白现在有2升酒,到酒店酒会加倍,碰到鲜花酒会减一升,现在给你n个酒店,m个花,问最后一个碰到的是花并且此时酒正好喝完的方案数

分析:

这个题当时一看就是dp,但是写了一个发现自己写崩了,索性就直接暴搜了,下面请看暴搜代码吧,dp思路会日后更新的

#include<iostream>
using namespace std;
const int mod = 1000000007,N = 20;
int n,m;
int dfs(int i,int j,int dang,int x){//这个dang代表的是当前遇到的是酒店还是花
	if(i > n || j > m) return 0;
	if(i == n && j == m) return x == 0 && dang == 1;
	int ans = dfs(i+1,j,0,x*2);
	if(x) ans = (ans + dfs(i,j+1,1,x-1)) % mod;
	return ans;
}
int main(){
	cin>>n>>m;
	cout<<dfs(0,0,0,2)<<endl;
	return 0;
}

试题 J: 砍竹子

后续更新…

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇智波一打七~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值