蓝桥杯刷题

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析(3)

在这里插入图片描述


👉🏻最大降雨量

原题链接:最大降雨量
解题思路:
这里假设7周的中位数分别为a,b,c,d,e,f,g。
这里e,f,g必然要大于中位数d。但我们如何实现让d最大呢?
想让d最大,我们换种角度看,就是49张牌中最少几张必须大于d,
显然我们可以看到后三周e,f,g后四天(包括e,f,g本身)这些必须大于d,还有d的后3天也必须大于d.
所以共有3x4+3 = 15天必须大于d,所以d的最大值为49-15 = 34.

👉🏻既约分数

在这里插入图片描述

#include <iostream>
using namespace std;
int fun(int a,int b)
{
  while(b)
  {
    int k = a%b;
    a = b;
    b = k;
  }
  return a;
}
int main()
{
  // 请在此输入您的代码
  int count = 0;
  for(int i = 1;i<=2020;i++)
  {
    for(int j = 1;j<=2020;j++)
    {
      if(fun(i,j)==1)
        count++;
    }
  }
  cout<<count<<endl;
  return 0;
}

👉🏻冶炼金属 (2023B组真题)

mycode:

#include<iostream>
#include<vector>
#include<math.h>
using namespace std;

int main()
{
    
    int N;
    cin >> N;
    vector<int> O(N), X(N);
    //先确定最大值
    int Max = 0;
    for (int i = 0; i < N; i++)
    {
        cin >> O[i] >> X[i];
        if (O[i] / X[i] > Max)
            Max = O[i] / X[i];
    }
    int finalmax = 0, finalmin = pow(10,9);
    for (int i = Max; i > 0; i--)
    {
        int flag = 1;
        for (int j = 0; j < N; j++)
        {
            if ( O[j]/i != X[j])
            {
                flag = 0;
                break;
            }
        }
        if (flag)
        {
            if (i > finalmax) finalmax = i;

            if (i < finalmin) finalmin = i;
        }
    }
    printf("%d %d\n", finalmin, finalmax);
  
    return 0;
}

👉🏻飞机降落(广度遍历和回溯)

mycode:

#include<iostream>
#include<vector>
#include<stdio.h>
using namespace std;
typedef struct Inf
{
	int t;
	int d;
	int l;
}inf;

bool check[10];//检查飞机是否被用过
vector<inf> v;

bool dfs(int sz, int last)
{
	if (sz == v.size())//走到这里说明已经有三个都可以了
		return true;
	for (int i = 0; i < v.size(); i++)
	{
		if (!check[i]&&v[i].t + v[i].d >= last)
		{
			check[i] = true;
			if (dfs(sz + 1, max(last, v[i].t) + v[i].l)) return true;

			//回溯恢复现场
			check[i] = false;
		}
	}
	return false;
}
int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int num;
		cin >> num;
		v.resize(num);
		for (int i = 0; i < num; i++)
			cin >> v[i].t >> v[i].d >> v[i].l;
		if (dfs(0, 0))
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
		memset(check, 0, sizeof check);
	}
	return 0;
}

#include <bits/stdc++.h>是万能头文件

👉🏻接龙数列

mycode1(广度遍历超时):

#include <bits/stdc++.h>
using namespace std;
int N;
vector<string> v;
int Min;// 删掉最少的数
bool erase[100000];//记录删除位置 
bool IsSerial(vector<string>& v)//判断是否为接龙数列 
{
    vector<string> tmp;
 
    for (int i = 0; i < N; i++)
    {
        if (erase[i] == false)
        {
            tmp.push_back(v[i]);
        }
    }
    int sz = (int)tmp.size();//注意:vector的size返回值是size_t
    if (sz >1)
    {
        for (int i = 0; i < sz-1; i++)
        {
            if (tmp[i].back() != tmp[i + 1].front())
                return false;
        }
    }
     
    return true;
}
void dfs()//广度遍历所有删除可能性 
{
    for (int i = 0; i < N; i++)
    {
        if (!erase[i])//如果不是接龙队列,删除
        {
             
            if (!IsSerial(v))
            {
                erase[i] = true;//将这个数删除
                dfs();
                //恢复现场
                erase[i] = false;
            }
            else
            {
                //此时已经删到是接龙队列了
                //遍历erase数组,记录true的数量,但是最后要再减1,因为再减1才能恢复接龙数列
                int num = 0;
                for (int i = 0; i < N; i++)
                {
                    if (erase[i] == true) num++;
                }
                if ( num < Min) Min = num; 
 
                break;//此时直接退出即可
            }
 
        }
    }
}
int main()
{
    cin >> N;
    v.resize(N);
    Min = N;
    for (int i = 0; i < N; i++)//26 61 14 41 15 
        cin >> v[i];
    if (!IsSerial(v))
        dfs();
    else
        Min = 0;//不用删了
    cout << Min << endl;
    return 0;
}

在这里插入图片描述

mycode2:(动态规划但仍然超时)

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	//dp[i]:表示到i位置及之前最长的接龙数列长度  14 47 27 75
	//dp[i] 特征方程表示: i之前的接龙数列长度一定是<=dp[i-1]的,所以dp[i]要么是dp[i-1]+1要么就是dp[i-1]
	//所以如何判断是否要加1很重要:我们先将dp表全部初始化为1,因为我们将每一个独立元素视为一个接龙队列,我们将每一个接龙队列最后一个元素称为龙头
	//每个龙头一开始都为1,但是我们在dp遍历过程中,我们要从当前位置向前再遍历,去寻找前面队列中能接龙上并且龙头值最大的队列,1+该龙头值即当前位置的最大龙头值
	//设这个表示龙头值的容器名为dragon,最后dp[i] = max(dragon[i],dp[i-1]);

	//最后结果就是N-dp[N-1]
	int N;
	cin >> N;
	vector<string> v(N);
	for (int i = 0; i < N; i++)
		cin >> v[i];
	vector<int> dp(N),dragon(N,1);
	//dp表初始化
	dp[0] = 1;//第一个位置肯定为1
	//开始填表
	for (int i = 1; i < N; i++)
	{
		for (int j = i-1; j >= 0; j--)
		{
			if (v[i].front() == v[j].back())
				dragon[i] = max(dragon[j] + 1, dragon[i]);
		}
		dp[i] = max(dragon[i], dp[i - 1]);
	}

	cout << N - dp[N - 1] << endl;
	
	

	return 0;
}

动态规划极简线性dp(参考答案)

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
int dp[10];
int main () {
    int n, mx = 0; cin >> n;
    for (int i = 0; i < n; i ++) {
        string s; cin >> s;
        int a = s[0] - '0', b = s.back() - '0';
        dp[b] = max(dp[b], dp[a] + 1), mx = max(mx, dp[b]);
    }
    cout << n - mx << endl;
    return 0;
}

👉🏻岛屿个数

mycode:

#include<bits/stdc++.h>
#include<iostream>
#include<vector>
using namespace std;
 
#define SZ 52
 
int M, N;
int island = 0;
bool check[SZ][SZ];
bool checkofspread[SZ][SZ];
char circle[SZ][SZ];
//上  下  左  右   左上   右上  左下  右下
int dx[8] = {-1,1,0,0,-1,1,-1,1};
int dy[8] = { 0,0,-1,1,-1,-1,1,1 };
 
 
bool spread(int row, int col)
{
    for (int i = 0; i < 8; i++)
    {
        int x = row + dx[i], y = col + dy[i];
        if (!(x >= 0 && x < M && y >= 0 && y < N))//跳出地图了
            return true;
        else if ((x >= 0 && x < M && y >= 0 && y < N) && !checkofspread[x][y] && circle[x][y] != 'o')
        {
            checkofspread[x][y] = true;
            return spread(x, y);
        }
    }
    //八方都不行的话,只能认命了
    return false;
}
 
bool dfs(vector<vector<int>>& vv,int row,int col,int initial[],int step)
{
    for (int i = 0; i < 4; i++)
    {
        int x = row + dx[i], y = col + dy[i];
        if (x >= 0 && x < M && y >= 0 && y < N && !check[x][y] && vv[x][y] == 1)//1.坐标合法  2.为1且未被check过
        {
            check[x][y] = true;
            step++;
            if (dfs(vv, x, y, initial, step))
            {
                circle[x][y] = 'o';
                return true;
            }
        }
        else if (x >= 0 && x < M && y >= 0 && y < N && check[x][y] && x == initial[0]&&y==initial[1] &&vv[x][y] == 1)//如果已经是被check过了,递归到了闭环,回到了原点
        {
             
            if(step>3)//步数至少大于3的时候才形成一个闭环
            return true;//说明是闭环
        }
    }
    //上下左右都走不了的话,说明该节点不属于环节点
    return false;
     
}
int main()
{
    int n;
    cin >> n;
    while (n--)
    {
         
        cin >> M >> N;
        vector<string> s(M);
        for (int i = 0; i < M; i++)
            cin >> s[i];
        vector<vector<int>> vv(M, vector<int>(N));
        for (int i = 0; i < M; i++)
            for (int j = 0; j < N; j++)
                 vv[i][j] = s[i][j]-'0';
        //遍历
        for (int i = 0; i < M; i++)
        {
            for (int j = 0; j < N; j++)
            {
                if (!check[i][j] && vv[i][j] == 1)
                {
                    check[i][j] = true;
                    int initial[] = { i,j };//这个记录最初出发的节点
                    //如果是环节点,递归后,一开始出发后会有两个回溯,step>3时,才是递归形成闭环
                    if (dfs(vv, i, j,initial,0))
                    {
                        circle[i][j] = 'o'; 
                    }
                    //现在就让从该位置向四面八法散开,遇到环节点不能扩散,直到有一个坐标能扩散出整个地图,说明该岛屿并没有被环绕住
                    if (spread(i, j))
                        island++;
 
                    memset(checkofspread, 0, sizeof checkofspread);
                }
            }
        }
        cout << island << endl;
        //重置初始化
        memset(check, 0,sizeof check);
        memset(circle, ' ', sizeof circle);
        island = 0;
    }
    return 0;

在这里插入图片描述

已经尽力了,太难了[苦]

👉🏻统计子矩阵

mycode(超出时间限制):

#include<iostream>
#include<vector>
using namespace std;
int GetSum(vector<vector<int>>& vv,int r,int c,int size_r,int size_c)
{
	int sum = 0;
	for(int i = r;i<=size_r;i++)
	{
		for(int j = c;j<=size_c;j++)
			sum+= vv[i][j];
	}
	return sum;
}
int main()
{
	int N,M,K;
	cin>>N>>M>>K;
	vector<vector<int>> vv(N+1,vector<int>(M+1));
	for(int i = 1;i<=N;i++)
	{
		for(int j = 1;j<=M;j++)
		{
			cin>>vv[i][j];
		}
	}
	int count = 0;
		for(int i = 1;i<=N;i++)
	{
			for(int j = 1;j<=M;j++)
		{
			//i*j阶
			for(int r = 1;r<=N;r++)
			{
				for(int c = 1;c<=M;c++)
				{
					if(r+i-1<=N&&c+j-1<=M&&GetSum(vv,r,c,r+i-1,c+j-1)<=K)
						count++;
				}
			} 
			int here;
		}
	}
	cout<<count<<endl;
	
	
	return 0;
}

参考优质代码:

#include<stdio.h>
int s[505][505];
int main( )
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            scanf("%d",&s[i][j]);
            s[i][j]+=s[i-1][j];
        }
    }
    long long int res=0;
    for(int i=1; i<=n; i++)
    {
        for(int j=i; j<=n; j++)
        {
            for(int left=1,right=1,sum=0; right<=m; right++)
            {
                sum+=s[j][right]-s[i-1][right];
                while (sum>k)
                {
                    sum-=s[j][left]-s[i-1][left];
                    left++;
                }
                res+=right-left+1;
            }
        }
    }
    printf("%lld",res);
    return 0;
}

此代码主要思路就是
1.算s[i][j]:第j列上,从第0行至第i行所有元素之和
2.进行区间遍历,[i,i~n],i>=1,i<=n;
3.区间遍历中包含列遍历,列遍历是在当前行区间内,统计所有和<k的子矩阵数量

代码复现:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int s[501][501];
	int n,m,k;
	cin>>n>>m>>k;
	for(int i = 1;i<=n;i++)
	{
		for(int j = 1;j<=m;j++)
		{
			cin>>s[i][j];
			s[i][j]+=s[i-1][j];//计算列之和 
		}
	}
	//进行区间遍历[i,j]行区间 
	long long  res = 0;
	for(int i = 1;i<=n;i++)
	{
		for(int j = i;j<=n;j++)
		{
			for(int left = 1,right = 1,sum = 0;right<=m;right++)//列遍历 
			{
				sum+=(s[j][right] - s[i-1][right]);//计算在right列上,[i,j]区间上的和
				while(sum>k)
				{
					sum-=(s[j][left] - s[i-1][left]);
					left++;
				//这里前缀和思想非常巧妙
				//在列遍历中,sum始终记录这该行区间内可能会出现的子矩阵之和
				//而不为了影响后续的列遍历
				//while循环内进行了断尾操作,比如我刚刚遍历第一列的元素没问题,但是到第二列sum>k了
				//此时就可以放心把第一列的子矩阵之和删了,反正你已经ok了,我继续看看第二列可不可以 ,以此类推 
				} 
				res+=(right-left+1);//这个res+=也很妙 
			}
		}
	 } 
	 cout<<res<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值