2022蓝桥杯c++B组

return 0;是个好习惯,在打蓝桥别的时候希望都加上!
我安慰我自己,没事,明年还有一次机会,咱们再接再厉!

填空题

第一题:九进制转十进制–数学

参考答案:1478

在这里插入图片描述
这个可以手算,也可以代码上手。

#include<bits/stdc++.h>

using namespace std;

const int N=10010;
int a[N];

int main()
{

 cout<<9*9*9*2+9*2+2<<endl;

    return 0;
}

第二题:顺子日期–手模

参考答案:14
在这里插入图片描述
这个答案我也不确定,我看到网上也有人给的参考答案是4,因为没有算012,我这里算了012,所以答案是14
0120、0121、0122、0123、0124、0125、0126、0127、0128、0129、1230、1231、1012、1123.

也可以是编程写法:y

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int months[] = {
    0, 31, 28, 31, 30, 31,
    30, 31, 31, 30, 31, 30,
    31
};

bool check(string str)
{
    for (int i = 0; i + 2 < str.size(); i ++ )
        if (str[i + 1] == str[i] + 1 && str[i + 2] == str[i] + 2)
            return true;

    return false;
}

int main()
{
    int year = 2022, month = 1, day = 1;

    int res = 0;
    for (int i = 0; i < 365; i ++ )
    {
        char str[10];
        sprintf(str, "%04d%02d%02d", year, month, day);

        if (check(str))
        {
            res ++ ;
            cout << str << endl;
        }

        if ( ++ day > months[month])
        {
            day = 1;
            month ++ ;
        }
    }

    cout << res << endl;
    return 0;
}


sprintf

sprintf最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf 在大多数场合可以替代itoa(把一个整数转换为字符串)。如:

// 把整数123打印成一个字符串保存在s中。

sprintf(s, "%d", 123); // 产生"123"

可以指定宽度,不足的左边补空格

sprintf(s, "%8d%8d", 123, 4567); // 产生:"    123    4567"

当然也可以左对齐

sprintf(s, "%-8d%8d", 123, 4567); // 产生:"123         4567"

也可以按照16进制打印

sprintf(s, "%8x", 4567); // 小写16进制,宽度占8个位置,右对齐

sprintf(s, "%-8X", 4568); // 大写16进制,宽度占8个位置,左对齐

2、控制浮点数打印格式
和printf的用法是一样的
3、连接字符串

char *who = "I";

char *whom = "52PHP";

 

sprintf(s, "%s love %s.", who, whom); // 产生:"I love 52PHP. "

或者

	
sprintf(s, "%.7s%.7s", a1, a2); // 产生:"ABCDEFGHIJKLMN"

4、打印地址信息
5、利用sprintf的返回值
详情可看大佬的博客!!!

编程题

第三题:刷题统计–模拟

在这里插入图片描述
送分题:
但是我写的不是很简洁。
写法一:(我的写法)

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;


int main()
{
    long long a,b,n;
  scanf("%lld%lld%lld",&a,&b,&n);
   ll total=a*5+b*2;
   ll ans=n/total;//学习的星期数
   ll res=n%total;//剩下的数
   ll d=0;
   for(int i=0;i<5;i++)
   {
       if(res>0)
       {
             res-=a;
             d++;
       }
       else break;
   }
   if(res>0)
   {
       for(int i=0;i<2;i++)
         {
       if(res>0)
       {
             res-=b;
             d++;
       }
       else break;
   }
   }
    d+=ans*7;
    printf("%lld",d);
    return 0;
}

写法二:y

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

int main()
{
    LL a, b, n;

    cin >> a >> b >> n;

    LL s = 5 * a + 2 * b;
    LL res = n / s * 7;
    n %= s;

    LL d[] = {a, a, a, a, a, b, b};
    for (int i = 0; n > 0; i ++ )
    {
        n -= d[i];
        res ++ ;
    }

    cout << res << endl;
    return 0;
}


第四题:修建灌木–规律

在这里插入图片描述
找规律这道题,也不是难题。

看数据范围,就知道不用考虑n==1的时候

#include<bits/stdc++.h>

using namespace std;

const int N=10010;
int a[N];

int main()
{
//    ios::sync_with_stdio(0);
//    cin.tie(0);cout.tie(0);
    int n;
    cin>>n;
    int l=1,r=n;
    for(int i=1;i<=n;i++)
    {
        a[i]=max((i-l)*2,(r-i)*2);
    }
    for(int i=1;i<=n;i++)
        cout<<a[i]<<endl;


    return 0;
}


第五题:X进制减法–模拟

在这里插入图片描述
在这里插入图片描述
这道题目我卡的点就是没考虑到a的某位-b的某位可能是负数!且不能纯暴力,这样子会tle。
在这里插入图片描述
1、(res+MOD)%MOD可以保证为非负数
2、max可以比较三个数,但是要加上大括号{(a,b,c)},然后,也可以比较vector里面的最大值
y:秦九韶算法

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010, MOD = 1000000007;

int n, m1, m2, m;
int a[N], b[N];

int main()
{
    scanf("%d", &n);
    scanf("%d", &m1);
    //我们这里从后往前存放数据,有点像高精度加法的存储方式
    for (int i = m1 - 1; i >= 0; i -- ) scanf("%d", &a[i]);
    scanf("%d", &m2);
    for (int i = m2 - 1; i >= 0; i -- ) scanf("%d", &b[i]);

    int m = max(m1, m2);

    int res = 0;
    for (int i = m - 1; i >= 0; i -- )
        res = (res * (LL)max({2, a[i] + 1, b[i] + 1}) + a[i] - b[i]) % MOD;//秦九韶算法

    printf("%d\n", res);//(res+MOD)%MOD可以保证为非负数,但是我们在前面已经确保啦
    return 0;
}

高精度加法板子

#include<iostream>
#include<vector>

using namespace std;
// 加引用避免再拷贝整个数组
vector<int> add(vector<int> &A, vector<int> &B) 
{
    // 定义储存结果的vector以及进位t,第0位没有进位->初始化为0
    vector<int> C;
    int t = 0;
    // 从个位开始遍历直至遍历完A和B的所有位
    for (int i = 0; i < A.size() || i < B.size(); i++)
    {
        // 每一次用t表示Ai,Bi与上一个数的进位这三个数的和
        if(i < A.size()) t += A[i];
        if(i < B.size()) t += B[i];
        // 当前这一位输出t除以10的余数
        C.push_back(t % 10);
        // t是否进位
        t /= 10;
    }
    // 如果最高位有进位则补1
    if(t) C.push_back(1);
    return C;
}
int main()
{
    // 使用字符串读入
    string a, b;
    vector<int> A, B;
    cin >> a >> b; //a = "123456"
    // 使用vector逆序读入,变成整数需要减去偏移量0
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');//A = [6,5,4,3,2,1]
    for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0');
    // 相当于vector<int>
    auto C = add(A, B);
    // 倒序输出
    for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
    return 0;
}

// 个位放在索引0的位置:出现进位的情况时需要在高位补上1,这样在数组后端补数比较容易

我的做法:出现了错误
略暴力

#include<bits/stdc++.h>

using namespace std;

const int N=1010;
const long long MOD=1000000007;
int a[N],b[N],c[N];

int main()
{
//    ios::sync_with_stdio(0);
//    cin.tie(0);cout.tie(0);
 int n,x;
 scanf("%d",&n);
 int A;
 scanf("%d",&A);//第一个数的位数
 for(int i=0;i<A;i++)
  {
      scanf("%d",&x);
      a[A-i]=x;
  }
 int B;
  scanf("%d",&B);//第二个数的位数
 for(int j=0;j<B;j++)
    {
      scanf("%d",&x);
      b[B-j]=x;
  }
  int ansA=0,ansB=0;
  c[0]=1;
 for(int i=1;i<=max(A,B);i++)
 {
     c[i]=max(2,max(a[i],b[i])+1);
     c[i]*=c[i-1]%MOD;
     ansA+=c[i-1]*a[i]%MOD;
     ansB+=c[i-1]*b[i]%MOD;
 }
 int ans =(ansA-ansB)%MOD;
 printf("%d",ans);
    return 0;
}

第六题:统计子矩阵–一维前缀和+思维

在这里插入图片描述
在这里插入图片描述
由于能用一维前缀和就用一维前缀和,像这种竞赛题目,一般都会卡常数。
然后数据范围是500,所以时间复杂度最大是 O ( n 3 ) O(n^3) On3
这里用到双指针,但是如果这里的数可能是负数的话,那么我就要考虑用平衡树啊,树状数组啊那些东西。
答案可能会爆int
C 5 2 C_5^2 C52 = 510 ∗ 255 = 130550 =510*255=130550 =510255=130550的平方就是int最大值

在这里插入图片描述
下面的代码可以看这个图片来理解。
慢指针l 继续前进(如图),直到 子矩阵的和 不大于k,慢指针没必要前进了,因为该子矩阵的所有宽度为 j - i + 1 的子矩阵(总共 r- l + 1 种)一定满足要求,更新该情况对答案的贡献 r - l+ 1;反之,如果慢指针r越界(r > l),则不操作,直接进入下层循环.
y:

#include<iostream>
using namespace std;

typedef long long ll;
const int N = 5e2+3;
int n, m, k;
int a[N][N];


int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m >> k;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            cin >> a[i][j];
            a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
        }
    }

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

    cout << ans << '\n';
}


我的tle做法:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=1010;
int a[N][N];
int s[N][N];
int  main()
{
   // freopen("in.txt","r",stdin);
    int n,m;
    ll k;
    int ans=0;
    scanf("%d%d%lld",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
      {
            scanf("%d",&a[i][j]);
          if(a[i][j]<=k) ans++;//1*1的矩阵
      }

        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];//二位前缀和矩阵
            //先来个暴力,拿部分分
//             for(int i=1;i<=n;i++)
//        for(int j=1;j<=m;j++)
//            cout<<s[i][j]<<endl;
    for(int i=1;i<n;i++)
    for(int j=1;j<m;j++)
    {
        for(int p=i;p<=n;p++)
            for(int q=j;q<=m;q++)
            {
                if(i==p&&j==q)continue;
//                cout<<p<<" " <<q<<" "<<i<< " " <<j<<endl;
//                cout<<s[p][q]-s[p][j-1]-s[i-1][q]+s[i-1][j-1]<<endl;
                if((s[p][q]-s[p][j-1]-s[i-1][q]+s[i-1][j-1])<=k)
            ans++;
            }
    }
    cout<<ans<<endl;
    return 0;
}

第七题:积木画–dp

类似题目!蒙德里安的梦想–状压dp
在这里插入图片描述
在这里插入图片描述
写法一:
图解题解,还是蛮清楚的!!牛!

#include<iostream>
using namespace std;

const int N = 1e7+10, MOD = 1000000007;
int f[N];
int main()
{
    f[1] = 1;
    f[2] = 2;
    f[3] = 5;
    int n;
    cin >> n;
    for(int i = 4; i <= n; i ++)
    {
        f[i] = (2*f[i-1]%MOD+f[i-3]%MOD)%MOD;
    }
    cout << f[n];
    return 0;
}

写法二:
递推思想牛!

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E7 + 10, mod = 1000000007;
int a[N]; // 当然你可以选择不开数组.
int main()
{
	int n; cin >> n;

	a[1] = 1, a[2] = 2, a[3] = 5;
    
	for (int i = 4; i <= n; ++i) {
		a[i] = a[i - 1]; // 加一列在后面
		a[i] = (a[i] + a[i - 2]) % mod;
		a[i] = (0ll + a[i] + a[i - 3] * 2) % mod;
	}

	cout << a[n] << endl;

	return 0;
}

y:
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e7 + 10, MOD = 1e9 + 7;

int n;
int g[4][4] = {
    {1, 1, 1, 1},
    {0, 0, 1, 1},
    {0, 1, 0, 1},
    {1, 0, 0, 0},
};
int f[2][4];

int main()
{
    scanf("%d", &n);
    f[1][0] = 1;

    for (int i = 1; i <= n; i ++ )
    {
        memset(f[i + 1 & 1], 0, sizeof f[0]);//滚动数组,优化空间
        for (int j = 0; j < 4; j ++ )
            for (int k = 0; k < 4; k ++ )
                f[i + 1 & 1][k] = (f[i + 1 & 1][k] + f[i & 1][j] * g[j][k]) % MOD;//这个乘法这里也可以优化
    }

    printf("%d\n", f[n + 1 & 1][0]);
    return 0;
}

第八题:扫雷–图论+哈希

在这里插入图片描述

在这里插入图片描述
这道题目是个图论.由于这个是有向边,不能用并查集。
图的遍历问题:dfs、bfs

手写哈希表的方式

#include<bits/stdc++.h>

using namespace std;

const int N=200003;//比2e5大的第一个质数
const int null=0x3f3f3f3f;

int h[N];

int  find(int x)
{
    int k=(x%N+N)%N;
    while(h[k]!=null&&h[k]!=x)
    {
        k++;
        if(k==N)k=0;
        //这个时候我们已经看完了,就要重头开始
    }
    return k;//要么是找到空位,要么是找到x
}



int main()
{
    int n;
    scanf("%d",&n);
    memset(h,0x3f,sizeof h);
    while(n--)
    {
        char op[2];
        int x;
        scanf("%s%d",op,&x);
        int k=find(x);
        if(*op=='I')
           h[k]=x;
        else {
            if(h[k]!=null) //如果是找到x
                puts("Yes");
            else puts("No");//如果是找到空位
        }
    }
    return 0;
}

手写哈希表会比unordered_map快一些。
哈希表是将一个(x,y)转化为一个比较小的正数的。
如果多个雷在同一个点的话,那我们就保留爆炸范围最大的点

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 50010, M = 999997;

int n, m;
struct Circle
{
    int x, y, r;
}cir[N];
LL h[M];//哈希表
int id[M];
bool st[M];

LL get_key(int x, int y)
{
    return x * 1000000001ll + y;
}

int find(int x, int y)
{
    LL key = get_key(x, y);
    int t = (key % M + M) % M;

    while (h[t] != -1 && h[t] != key)
        if ( ++ t == M)
            t = 0;
    return t;
}

int sqr(int x)
{
    return x * x;
}

void dfs(int x, int y, int r)
{
    st[find(x, y)] = true;

    for (int i = x - r; i <= x + r; i ++ )
        for (int j = y - r; j <= y + r; j ++ )
            if (sqr(i - x) + sqr(j - y) <= sqr(r))
            {
                int t = find(i, j);
                if (id[t] && !st[t])
                    dfs(i, j, cir[id[t]].r);
            }
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++ )
    {
        int x, y, r;
        scanf("%d%d%d", &x, &y, &r);
        cir[i] = {x, y, r};

        int t = find(x, y);
        if (h[t] == -1) h[t] = get_key(x, y);

        if (!id[t] || cir[id[t]].r < r)
            id[t] = i;
    }

    while (m -- )
    {
        int x, y, r;
        scanf("%d%d%d", &x, &y, &r);

        for (int i = x - r; i <= x + r; i ++ )
            for (int j = y - r; j <= y + r; j ++ )
                if (sqr(i - x) + sqr(j - y) <= sqr(r))
                {
                    int t = find(i, j);
                    if (id[t] && !st[t])
                        dfs(i, j, cir[id[t]].r);
                }
    }

    int res = 0;
    for (int i = 1; i <= n; i ++ )
        if (st[find(cir[i].x, cir[i].y)])
            res ++ ;

    printf("%d\n", res);
    return 0;
}


应该算是我的错误暴力写法了哈哈哈

#include<bits/stdc++.h>

using namespace std;
const int N=50010;
struct B{
int x,y,r;
bool k;
bool operator<(const B&t)const
{
    if(t.x!=x)return x<t.x;
    else return y<t.y;
}
};
B a[N];
int dist(int x1,int y1,int x2,int y2)
{
    int res=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    return res;
}
int main()
{
        ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n,m,x,y,r,ans=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>x>>y>>r;
        a[i].x=x,a[i].y=y,a[i].r=r;
    }
    sort(a+1,a+n+1);
    while(m--)
    {
    cin>>x>>y>>r;
    for(int i=1;i<=n;i++)
    {
        int tt=dist(a[i].x,a[i].y,x,y);
        if(tt>10)break;
        if(a[i].k==false&&tt<r)
        {
              ans++;
              a[i].k=true;
              for(int j=i;j<=n;j++)
              {
                  int kk=dist(a[i].x,a[i].y,a[j].x,a[j].y);
                  if(kk>10)break;
                  if(a[j].k==false&&kk<a[i].r)
              {
                  ans++;
                  a[j].k=true;
              }
              }

        }
    }
    }
    cout<<ans<<endl;
    return 0;
}

第九题:李白打酒加强版–dp

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

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110, MOD = 1e9 + 7;

int n, m;
int f[N][N][N];

int main()
{
    cin >> n >> m;

    f[0][0][2] = 1;
    for (int i = 0; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ )
            for (int k = 0; k <= m; k ++ )
            {
                int& v = f[i][j][k];
                if (i && k % 2 == 0) v = (v + f[i - 1][j][k / 2]) % MOD;
                if (j) v = (v + f[i][j - 1][k + 1]) % MOD;
            }

    cout << f[n][m - 1][1] << endl;
    return 0;
}

第十题:砍竹子–栈、优先队列

在这里插入图片描述
在这里插入图片描述
一个数只能变小,不能变大,所以我们就从最大的数开始入手,然后,可以用贪心的算法。
我们可以用优先队列来写,在优先队列里面维护一段区间。v和区间左端点是我们的双端点排序。
第二个写法比第一个写法好理解、且运行速度快一些!
写法一:O(6nlogn)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;

typedef long long LL;
const int N = 200010;

int n;
LL h[N];

struct Seg
{
    int l, r;
    LL v;
    bool operator< (const Seg& S) const
    {
        if (v != S.v) return v < S.v;
        return l > S.l;
    }
};

LL f(LL x)
{
    return sqrt(x / 2 + 1);
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%lld", &h[i]);

    priority_queue<Seg> heap;

    for (int i = 0; i < n; i ++ )
    {
        int j = i + 1;
        while (j < n && h[i] == h[j]) j ++ ;
        heap.push({i, j - 1, h[i]});
        i = j - 1;
    }

    int res = 0;
    while (heap.size() > 1 || heap.top().v > 1)
    {
        auto t = heap.top();
        heap.pop();

        while (heap.size() && heap.top().v == t.v && t.r + 1 == heap.top().l)
        {
            t.r = heap.top().r;
            heap.pop();
        }
        heap.push({t.l, t.r, f(t.v)});

        if (t.v > 1) res ++ ;
    }

    printf("%d\n", res);

    return 0;
}

写法二:O(6n)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long LL;

const int N = 200010, M = 10;

int n, m;
LL f[N][M];

int main()
{
    scanf("%d", &n);
    LL stk[M];

    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        LL x;
        int top = 0;
        scanf("%lld", &x);

        while (x > 1) stk[ ++ top] = x, x = sqrt(x / 2 + 1);
        res += top;//记录他的步长
        m = max(m, top);

        for (int j = 0, k = top; k; j ++, k -- )
            f[i][j] = stk[k];
    }

    for (int i = 0; i < m; i ++ )
        for (int j = 1; j < n; j ++ )
            if (f[j][i] && f[j][i] == f[j - 1][i])
                res -- ;

    printf("%d\n", res);
    return 0;
}

总结

这一次做的很感觉做的很赶,很多题目都是暴力写出来的,然后本来打算把能写的弄完之后再回头看看改一下的,根本不给我这机会,好家伙!
能力的确有待提高,知道都是很基础的题,呜呜呜,我们学校的金牌爷提前一个半小时就走了,真的是慕了,自己要继续努力哇!

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值