2022蓝桥杯B组C++真题题解(二)

简介: 2022蓝桥杯B组C++真题题解

少写了一个循环,用了cmach库的max函数,少开了几个数组,终于100了;


F:  统计子矩阵  


本题总分:15


问题描述

给定一个N×M 的矩阵 A, 请你统计有多少个子矩阵 (最小 1×1, 最大 N×M) 满足子矩阵中所有数的和不超过给定的整数 K?


输入格式

第一行包含三个整数 N, M,和 K.


之后 N 行每行包含 M 个整数, 代表矩阵 A.


输出格式

一个整数代表答案。


样例输入

3 4 10
1 2 3 4
5 6 7 8
9 10 11 12

样例输出

19

样例说明

满足条件的子矩阵一共有 19 , 包含:


大小为 1×1 的有 10 个。


大小为 1×2 的有 3 个。


大小为 1×3 的有 2 个。


大小为 1×4 的有 1 个。


大小为 2×1 的有 3 个。


评测用例规模与约定

对于 30% 的数据, N,M≤20.


对于 70% 的数据, N,M≤100.


对于 100% 的数据, 1≤N,M≤500;0≤Aij≤1000;1≤K≤250000000.


运行限制

最大运行时间:1s

最大运行内存: 256M

本题为二维前缀和问题,首先纯暴力要6个for循环,肯定超时,用动态规划dp,将其二维矩阵和记录下来(以(1,1)为左上点的矩阵)。可以减少2个for循环,以空间换时间;

#include <iostream>
using namespace std;
int n,m,k;
int dp[505][505];
int zanshi,ans;
int main()
{
  cin>>n>>m>>k;
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
      cin>>zanshi;
      dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+zanshi;   //记录前二维矩阵和
    }
  }
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
      for(int x=i;x<=n;x++){
        for(int y=j;y<=m;y++){
          if(dp[x][y]-dp[x][j-1]-dp[i-1][y]+dp[i-1][j-1]<=k){  //求包括中间的矩阵和
            ans++;
          }
        }
      }
    }
  }
  cout<<ans;
  return 0;
}

嗯,怎么说,四个for循环时间复杂度还是高了点。再怎么优化呢?你想如果一个小的矩阵大于k的话,那么包含该矩阵大矩阵一定大于k,还是有优化空间的,优化一下。

#include <iostream>
using namespace std;
int n,m,k;
int dp[505][505];
long long zanshi,ans;   //ans要long long,
int main()
{
  cin>>n>>m>>k;
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
      cin>>zanshi;
      dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+zanshi;
    }
  }
  for(int i=1;i<=n;i++){
    for(int j=i;j<=n;j++){
      for(int l=1,r=1;r<=m;r++){
        while(l<=r&&(dp[j][r]-dp[j][l-1]-dp[i-1][r]+dp[i-1][l-1]>k)){
          l++;
        }
        if(r>=l){
          ans+=r-l+1;
        }
      }
    }
  }
  cout<<ans;
  return 0;
}

终于100了,重点说一下以下代码,某个大佬写的,看了好久才看懂。

for(int i=1;i<=n;i++){
    for(int j=i;j<=n;j++){
      for(int l=1,r=1;r<=m;r++){
        while(l<=r&&(dp[j][r]-dp[j][l-1]-dp[i-1][r]+dp[i-1][l-1]>k)){
          l++;
        }
        if(r>=l){
          ans+=r-l+1;
        }
      }
    }
  }

首先前两层for循环,可以把i,j(横)看做两个木棍,初始化都是1,然后j向下运动,到达底端后,i下降到2,j返回到2,以此类推,最后i,j都到达n结束;再说第三层for循环。初始化l,r也是


看做两个木棍(竖),初始化都为1,也就是矩阵最左边。当r(右棍)>=l(左棍)时,判断四个木棍围成的矩阵是否符合小于k,大于k时左棍向右移动,矩阵变小。直到小于k,小于k时将可行方案加到总方案数种。


最最重要的是ans+=r-l+1;这行代码,我们只考虑列,因为行还在移动。如果r棍不动,那么l棍向r棍靠近,直到l=r。比如r=3,l=1,那么有三个单列矩阵,两个双列矩阵,一个三列矩阵,一共六个解。那么你可以算算,l第一次移动时ans=3,直到l=r,ans=6,刚刚好。但是当r向右移动时就比较复杂了,有两个大矩阵重合,此时ans的值应该等于两个大矩阵解的和减去中间重合矩阵(重合最大的矩阵),比如两个大矩阵边长分别是5和6,中间重合最大矩阵为3,此时ans解为5+4+3+2+1+6+5+4+3+2+1-3-2-1=6+5+4+3+2+1+5+4;你会发现正好是移动后矩阵解加上之前移动矩阵前加上的解。

G:  积木画

本题总分:15

问题描述

小明最近迷上了积木画, 有这么两种类型的积木, 分别为 II 型(大小为 2 个单位面积) 和 LL 型 (大小为 3 个单位面积):同时, 小明有一块面积大小为 2×N 的画布, 画布由 2×N个 1×1 区域构 成。小明需要用以上两种积木将画布拼满, 他想知道总共有多少种不同的方式? 积木可以任意旋转, 且画布的方向固定。


输入格式

输入一个整数 NN,表示画布大小。


输出格式

输出一个整数表示答案。由于答案可能很大,所以输出其对 1000000007 取模后的值。


样例输入

3

样例输出

5

样例说明

五种情况如下图所示,颜色只是为了标识不同的积木:

评测用例规模与约定

对于所有测试用例,1≤N≤10000000.

运行限制

语言 最大运行时间 最大运行内存
C++ 2s 256M
C 2s 256M
Java 5s 256M
Python3 10s 256M

动态规划题目,难点在于寻找递推公式。

递推公式推理过程

不过里面有个小错误,不知道大家发现没有

这个结论正确,不过过程有个错误,

应该还要考虑没有剩余时的特殊情况。

#define m 1000000007
int n;
long long dp[10000005];
int main()
{
  cin>>n;
  dp[0]=1;
  dp[1]=1;
  dp[2]=2;
  dp[3]=5;
  for(int i=4;i<=n;i++){
    for(int j=0;j<=i-1;j++){
      dp[i]+=2*dp[j];
      if(j==i-1 || j==i-2){
        dp[i]=dp[i]-dp[j];
      }
    }
  }
  cout<<(dp[n])%m;
  return 0;
}

  

答案正确,但是存在超时和爆调的问题,通过率为0,爆 栈后答案就不对了。

#include <iostream>
using namespace std;
#define m 1000000007
int n;
long long dp[10000005];
int main()
{
  cin>>n;
  dp[0]=1;
  dp[1]=1;
  dp[2]=2;
  dp[3]=5;
  for(int i=4;i<=n;i++){
    dp[i]=(dp[i-3]%m+2*dp[i-1]%m)%m;
  }
  cout<<(dp[n])%m;
  return 0;
}

这个时间复杂度低,重要的是没有减法,可以任意取模,不存在爆栈问题;

H:  扫雷


本题总分:20


问题描述

小明最近迷上了一款名为《扫雷》的游戏。其中有一个关卡的任务如下, 在一个二维平面上放置着 n 个炸雷, 第 i 个炸雷 (xi,yi,ri) 表示在坐标 (xi,yi) 处存在一个炸雷, 它的爆炸范围是以半径为 r的一个圆。


为了顺利通过这片土地, 需要玩家进行排雷。玩家可以发射 m 个排雷火 箭, 小明已经规划好了每个排雷火箭的发射方向, 第 j 个排雷火箭(xj,yj,rj) 表 示这个排雷火箭将会在 (xj,yj) 处爆炸, 它的爆炸范围是以半径为 r 的一个圆, 在其爆炸范围内的炸雷会被引爆。同时, 当炸雷被引爆时, 在其爆炸范围内的 炸雷也会被引爆。现在小明想知道他这次共引爆了几颗炸雷?


你可以把炸雷和排雷火箭都视为平面上的一个点。一个点处可以存在多个 炸雷和排雷火箭。当炸雷位于爆炸范围的边界上时也会被引爆。


输入格式

输入的第一行包含两个整数 n 、m


接下来的 n行, 每行三个整数 xi,yi,ri, 表示一个炸雷的信息。


再接下来的 m 行, 每行三个整数 xj,yj,rj, 表示一个排雷火箭的信息。


输出格式

输出一个整数表示答案。


样例输入

2 1
2 2 4
4 4 2
0 0 5

样例输出

2

样例说明

示例图如下,排雷火箭 1 覆盖了炸雷 1,所以炸雷 1 被排除;炸雷 1 又覆盖了炸雷 2,所以炸雷 2 也被排除。

评测用例规模与约定

运行限制

最大运行时间:1s

最大运行内存: 256M】
本题是我采用dfs算法,递归遍历。先遍历每一个火箭,然后搜寻每个炸弹,符合条件的炸弹进入dfs引爆;

#include <iostream>
#include <cmath>
using namespace std;
int n,m;
int ans;
struct zhadan{
  int x,y;
  int r;
  zhadan(){}
  zhadan(int xx,int yy,int rr){
    x=xx;
    y=yy;
    r=rr;
  }
};
float dd(zhadan a,zhadan b){
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
zhadan a[50005];
zhadan b[50005];
bool c[50005];
void dfs(zhadan v){
  for(int i=0;i<n;i++){
    if(dd(a[i],v)<=v.r && !c[i]){
      c[i]=1;
        ans++;
        dfs(a[i]);
    }
  }
}
int main(){
  cin>>n>>m;
  for(int i=0;i<n;i++){
    cin>>a[i].x>>a[i].y>>a[i].r;
  }
  for(int i=0;i<m;i++){
    cin>>b[i].x>>b[i].y>>b[i].r;
  }
  for(int i=0;i<m;i++){
    dfs(b[i]);
  }
  cout<<ans;
  return 0;
}

40通过率,再优化一下,我发现炸弹个数很多,但是爆照范围很小,我们尝试遍历边长为2r的正方形,而不是炸弹个数。遍历整个图形用bfs更好。

#include <iostream>
#include <map>
#include <set>
#include <queue>
using namespace std;
const int N=50005;
int ans;
int n,m;
struct node
{
    int x;
    int y;
    int r;
    node(int xx,int yy,int rr)
    {
        x=xx;
        y=yy;
        r=rr;
    }
};
int get_len(int x,int y,int i,int j)
{
    return (x-i)*(x-i)+(y-j)*(y-j);
}
map<pair<int,int>,int> mp;
set<pair<int,int>> s;
queue<node> q;
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        int x,y,r;
        cin>>x>>y>>r;
        int tmp=mp[{x,y}]+100; 
        mp[{x,y}]=max(tmp,tmp/100*100+r);
    }
    for(int i=0;i<m;i++)
    {
        int x,y,r;
        cin>>x>>y>>r;
        q.push(node(x,y,r));
    }
    int ans=0;
    while(q.size())
    {
        int xx=q.front().x;
        int yy=q.front().y;
        int rr=q.front().r;
        q.pop();
        for(int i=xx-rr;i<=xx+rr;i++)
        {
            for(int j=yy-rr;j<=yy+rr;j++)
            {
                pair<int,int> p(i,j);
                if(s.count(p))
                {
                    continue;
                }
                if(!mp.count(p))
                {
                    continue;
                }
                if(get_len(xx,yy,i,j)>rr*rr)
                {
                    continue;
                }
                s.insert(p);
                q.push(node(i,j,mp[p]%100));
                ans=ans+mp[p]/100;
            }
        }
    }
    cout<<ans;
}


相关文章
|
1天前
|
算法 测试技术 C++
小唐开始刷蓝桥(八)2013年第四届C/C++ B组蓝桥杯省赛真题
小唐开始刷蓝桥(八)2013年第四届C/C++ B组蓝桥杯省赛真题
|
1天前
|
算法 C++ 数据格式
小唐开始刷蓝桥(七)2014年第五届C/C++ B组蓝桥杯省赛真题
小唐开始刷蓝桥(七)2014年第五届C/C++ B组蓝桥杯省赛真题
|
1天前
|
算法 C++
小唐开始刷蓝桥(六)2015年第六届C/C++ B组蓝桥杯省赛真题
小唐开始刷蓝桥(六)2015年第六届C/C++ B组蓝桥杯省赛真题
|
1天前
|
算法 C++
小唐开始刷蓝桥(五)2016年第七届C/C++ B组蓝桥杯省赛真题
小唐开始刷蓝桥(五)2016年第七届C/C++ B组蓝桥杯省赛真题
|
1天前
|
存储 人工智能 算法
小唐开始刷蓝桥(四)2017年第八届C/C++ B组蓝桥杯省赛真题
小唐开始刷蓝桥(四)2017年第八届C/C++ B组蓝桥杯省赛真题
|
1天前
|
机器学习/深度学习 存储 人工智能
小唐开始刷蓝桥(三)2018年第九届C/C++ B组蓝桥杯省赛真题
小唐开始刷蓝桥(三)2018年第九届C/C++ B组蓝桥杯省赛真题
|
1天前
|
存储 人工智能 Java
小唐开始刷蓝桥(二)2019年第十届C/C++ B组蓝桥杯省赛真题
小唐开始刷蓝桥(二)2019年第十届C/C++ B组蓝桥杯省赛真题
|
1天前
|
人工智能 搜索推荐 C++
小唐开始刷蓝桥(一)2020年第十一届C/C++ B组第二场蓝桥杯省赛真题
小唐开始刷蓝桥(一)2020年第十一届C/C++ B组第二场蓝桥杯省赛真题
|
2天前
|
存储 人工智能 算法
第十四届蓝桥杯C++B组编程题题目以及题解
第十四届蓝桥杯C++B组编程题题目以及题解
|
8天前
|
测试技术
消除游戏(第十三届蓝桥杯省赛C++C组 , 第十三届蓝桥杯省赛PythonA/B/研究生组)
消除游戏(第十三届蓝桥杯省赛C++C组 , 第十三届蓝桥杯省赛PythonA/B/研究生组)
消除游戏(第十三届蓝桥杯省赛C++C组 , 第十三届蓝桥杯省赛PythonA/B/研究生组)