又是一年一度的蓝桥杯了,本届蓝桥杯的题型分布有所改变,如果没记错的话往常都是3-5道填空题,今年一反往常只有2道填空题了,相对于之前的“暴力破解杯”和“dp杯”,现在的蓝桥杯阅读难度有所降低,数学知识和贪心的题目也多了起来。
第一题:试题A: 九进制转十进制
题解:最近几年的打卡题都偏向于计算机基础知识了,这题如果是科班出身的话在计算机组成原理的课程中应该有学习到进制转换的知识,93*2+92*0+90*2=1478
#include
using namespace std;
int main()
{
cout<<9*9*9*2+9*2+1*2;
return 0;
}
运行结果
第二题:试题B: 顺子日期
题解:题目好像有歧义,题目中说20221023不算顺子,但是里面又含有210,那么到底是逆序不算顺子还是含0不算顺子呢。由题解中的解释:顺子就是连续的三个数字,所以递增递减都应该算作顺子,所以2022年01月20日到2022年01月29日(不确定)、2022年03月21日,2022年02月10日(不确定),2022年10月12日(不确定)2022年11月23,2022年12月10日(不确定),2022年12月30日,2022年12月31日,一共4种?。
第三题:试题C: 刷题统计
题解:一看数据规模n<=1018,这在往常的蓝桥杯里比较少见,第三题就开始卡时间了,直接模拟的话只能拿一半的分,所以可以采用数学方法来解,先算一周能做多少工作,在计算总共需求多少周,然后在讨论剩下的部分
#include
using namespace std;
int main()
{
//注意题目中的数据规模,明显数据范围是大于int的最大值
//如果这里使用int的话,就算想到了用数学方法解也会因为接收不到n导致错误
long long a,b,n;
cin>>a>>b>>n;
int totalday;
//先计算一周能做多少题目
int weekjob=a*5+b*2;
//一共需要多少周
int week=n/weekjob;
//剩余的题目
double dayjob=n%weekjob;
if(dayjob<=5*a){
totalday=week*7+ceil(dayjob/a);//向上取整
}else{
//如果剩余的工作周一到周五做不完则周六周日做
totalday=week*7+5+ceil((dayjob-5*a)/b);
}
cout<<totalday;
return 0;
}
第四题:试题D: 修剪灌木
题解:老规矩先看数据规模n<=10000,所以来说正常用模拟的方法不会超时,除了模拟以外还可以找规律贪心很容易可以知道树的最大高度=max((i-1)*2,(n-i)*2);然后树的最大高度是对称的。
解法一(贪心):
#include
using namespace std;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cout<<max((i-1)*2,(n-i)*2)<<endl;
}
解法二(模拟):
#include
using namespace std;
void all_tree_grow_up(vector<int> &trees,vector<int> &res,int k){
for(int i=1;i<trees.size();i++){
if(i==k)continue;
++trees[i];
res[i]=trees[i]>=res[i]?trees[i]:res[i];
}
}
int main()
{
int n;
cin>>n;
vector<int> trees(n+1,0);
vector<int> res(n+1);
int k=0;
//循环三遍就能找到树的最大高度
while(k<3){
//除了剪了的那棵树,其他的树长高1cm
for(int i=1;i<trees.size();i++){
trees[i]=0;
all_tree_grow_up(trees,res,i);
}
for(int i=trees.size()-1;i>=1;i--){
trees[i]=0;
all_tree_grow_up(trees,res,i);
}
k++;
}
for(int i=1;i<res.size();i++){
cout<<res[i]<<endl;
}
return 0;
}
第五题:试题E: X 进制减法
题解:看了半天,没看太懂,感觉题面有点问题,等老师通知改题,结果题面没有问题
第六题:F:统计子矩阵
题解:二维前缀和问题,不加优化的话时间复杂度是O(n3),只能通过70%的样例。
解法一(BF)
#include
using namespace std;
bool sum_matrix(vector<vector<int>> &nums,int x,int y,int right,int down,int k){
int sum=0;
for(int i=x;i<=right;i++){
for(int j=y;j<=down;j++){
sum+=nums[i][j];
if(sum>k){
return false;
}
}
}
return true;
}
int main()
{
int n,m,k;
cin>>n>>m>>k;
vector<vector<int>> nums(n,vector<int>(m));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>nums[i][j];
}
}
int cnt=0;
//i,j为矩阵右上角的坐标,p,q为矩阵的长宽
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
//小矩阵不能超过大矩阵的边界
for(int p=0;p+i<n;p++){
for(int q=0;q+j<m;q++){
if(sum_matrix(nums,i,j,p+i,q+j,k)){
cnt++;
}
}
}
}
}
cout<<cnt;
return 0;
}
解法二(前缀和):
#include
#include
#include
using namespace std;
const int N = 510;
int q[N][N],p[N][N];
int n,m,k;
int main(){
cin >> n >> m >> k;
for(int i = 1; i <= n; i ++)
{
for (int j = 1; j <= m; j++)
{
scanf("%d", &q[i][j]);
p[i][j] = p[i-1][j]+p[i][j-1]-p[i-1][j-1]+q[i][j];
}
}
long long res = 0;
for (int l = 1; l <= m; l ++)
{
for (int r = l; r <= m; r ++)
{
for (int i = 1, j = 1; i <= n; i ++)
{
while (j <= i && p[i][r] - p[i][l - 1] - p[j - 1][r] + p[ j - 1][ l - 1] > k) j ++;
if (j <= i) res=res+(i - j + 1);
}
}
}
cout << res << endl;
return 0;
}
第七题:试题G:积木画
题解:很明显的一个dp题目,可以分三种情况分类讨论,具体的状态转移方程本蒟蒻 不确定是不是对的,所以这题的题解就不发了。
#include
using namespace std;
int cnt=0;
void clear_booms(vector<vector<double>> &bombs,double x,double y,double r){
if(bombs.empty()){
return;
}
for(int i=0;i<bombs.size();i++){
double x1=bombs[i][0]-x;
double y1=bombs[i][1]-y;
double r1=sqrt(pow(x1,2)+pow(y1,2));
if(r1<=r){
++cnt;
//爆炸之后删除当前的雷,防止重复统计
bombs.erase(bombs.begin()+i);
clear_booms(bombs,bombs[i][0],bombs[i][1],bombs[i][2]);
}
}
}
int main()
{
int n,m;
cin>>n>>m;
vector<vector<double>> bombs(n,vector<double>(3));
vector<vector<double>> rockets(m,vector<double>(3));
for(int i=0;i<n;i++){
cin>>bombs[i][0]>>bombs[i][1]>>bombs[i][2];
}
for(int i=0;i<m;i++){
cin>>rockets[i][0]>>rockets[i][1]>>rockets[i][2];
}
for(int i=0;i<m;i++){
clear_booms(bombs,rockets[i][0],rockets[i][1],rockets[i][2]);
}
cout<<cnt;
return 0;
}
第九题:李白大酒加强版
题解:当时只想到用dfs+剪枝来解决,这题可以算是一个有附加条件的排列组合题,下面这个题解应该能拿到一半的分,剪枝的效率不太高。
#include
using namespace std;
int cnt=0;
void dfs(int n,int m,int wire){
//当遇到的花满足m次但还剩店没访问完
if(m==0&&n>0){
return;
}
//当酒没了,当时还有花没访问完
if(wire==0&&m>=0){
return;
}
//当酒的数量达到最大还不够喝时
if(pow(2,n)*wire<m){
return;
}
if(n==0&&m==wire){
++cnt;
return;
}else if(n==0&&m!=wire){
return;
}
// if(n==0&&m==1&&wire==1){
// ++cnt;
// return;
// }
if(wire>=1){
dfs(n,m-1,wire-1);
}
dfs(n-1,m,wire*2);
}
int main()
{
int n,m;
cin>>n>>m;
if(pow(2,n+1)<m){
cout<<0;
return 0;
}
dfs(n,m,2);
cout<<cnt;
return 0;
}
题解:最长公共下降子序列问题,没有遇到过。