2022年蓝桥杯初赛B组全解

九进制转十进制

题目链接P2031 - [蓝桥杯2022初赛] 九进制转十进制 - New Online Judge (ecustacm.cn)

思路:简单的进制转换问题,自己算出来答案1487输出即可

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main() 
{
    cout << "1478" << endl;
    return 0;
}

顺子日期

题目链接P2032 - [蓝桥杯2022初赛] 顺子日期 - New Online Judge (ecustacm.cn)

思路:枚举。因为在2022年中的顺子日期十分有限,分别将其枚举出来。为20221123 20221012 20221231 20221230 20220120-20220129总共十四个,直接输出答案

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main() 
{
    cout << "14" << endl;
    return 0;
}
/**************************************************************
    Problem: 2032
    User: 2317998220 [Angus]
    Language: C++
    Result: 正确
    Time:1 ms
    Memory:1428 kb
****************************************************************/

刷题统计

题目链接P2033 - [蓝桥杯2022初赛] 刷题统计 - New Online Judge (ecustacm.cn)

思路:计算出一周总共能刷多少题,用题目总数整除一周能刷的题目,取余得出剩余的题目。对剩余的题目进行判断即可。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
typedef long long ll;
ll a, b, n, ans;
int main() 
{
    cin >> a >> b >> n;
    ans = n / (5 * a + 2 * b);
    ans *= 7;
    ll res = n % (5 * a + 2 * b);
    if (res < 5 * a)
    {
        if (res % a == 0)
        {
            ans += (res / a);
        }
        else
        {
            ans += (res / a) + 1;
        }
    }
    else
    {
        ans += 5;
        res -= 5 * a;
        if (res % b == 0)ans += (res / b);
        else ans += (res / b) + 1;
    }
    cout << ans << endl;
    return 0;
}

修建灌木

题目链接P2034 - [蓝桥杯2022初赛] 修剪灌木 - New Online Judge (ecustacm.cn)

思路:

假设我们要求红色的树最高能生长多高,只有两种情况,从又左向右剪再折返,由右向左剪再折返。在这两种情况能生长的最大高度去一个max即可

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
typedef long long ll;
int n;
int main() 
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int ans = max((n - i) * 2, (i-1)*2);
        cout << ans << endl;
    }
    return 0;
}

X进制减法

题目链接P2035 - [蓝桥杯2022初赛] X进制减法 - New Online Judge (ecustacm.cn)

思路:由题目意思可以知道X进制转化为10进制的方法使每个数位的数乘上前几个数位的进制的累乘。

如果两个数相减,如:(A1-B1)x1+(A2-B2)x2+(A3-B3)x3+……我们可以知道Ai-Bi是固定的,所以只需要让xi尽可能小即可,但每个数位的进制肯定比这个数位上的数字大,所以我们只要取

max(2,max(Ai,Bi)+1)即可。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 100010;
const int M = 1e9 + 7;
int a[N], b[N], c[N];
int n, m1,m2;
int main()
{
    cin >> n;
    cin >> m1;
    for (int i = m1; i >0; i--)
    {
        cin >> a[i];
    }
    cin >> m2;
    for (int i = m2; i >0; i--)
    {
        cin >> b[i];
    }
    int len1 = m1;
    int len2 = m2;
    for (int i = len1; i>0; i--)
    {
        c[i] = max(2, max(a[i], b[i])+1);
    }
    ll ansA = 0, ansB = 0;
    for (int i = len1; i >0; i--)
    {
        ansA = (ansA * c[i] + a[i]) % M;
    }
    for (int i = len2; i >0; i--)
    {
        ansB = (ansB * c[i] +b[i]) % M;
    }
    ll ans = (ansA - ansB + M) % M;
    cout << ans << endl;
    return 0;
}

统计子矩阵

题目链接P2036 - [蓝桥杯2022初赛] 统计子矩阵 - New Online Judge (ecustacm.cn)

思路:二维数组前缀和+双指针。

首先前缀和肯定是必不可少的,如果暴力四层for循环的话时间复杂度就到了O(n^4)很显然会超时。

我们可以换一个思路来想,x1和x2我们依旧用循环。但是我们可以注意到如果出现了一个矩阵内的和如果大于所给条件k,因为矩阵内不存在负数,所以想要继续满足条件,则y1必定向右移动,这和y2的移动方向是一样的,所以我们可以用双指针来写。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdio.h>
using namespace std;
const int N = 510;
typedef long long ll;
ll a[N][N], s[N][N],ans;
int n, m,k;
ll qianzhui(int x1, int y1, int x2, int y2)
{
    ll res =s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
    return res;
}
int main() 
{
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
             scanf("%ld",&a[i][j]);
        }
    }
    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 = i; j <= n; j++)
        {
            for (int l = 1, r = 1; r <= m; r++)
            {
                while (l <= r && qianzhui(i, l, j, r) > k)l++;
                ans += r - l + 1;
            }
        }
    printf("%ld", ans);
    return 0;
}

积木画

题目链接P2037 - [蓝桥杯2022初赛] 积木画 - New Online Judge (ecustacm.cn)

思路:动态规划。dp[i]中的i为第几列。

我们首先观察dp[i]可以由哪几个状态转移过来。

dp[i-1]是一种状态且只有一种方法。

dp[i-2]是一种状态且只有一种方法。

dp[i-3]是一种状态且有两种方法。

dp[i-4]是一种状态且有两种方法。

经过画图可以得知dp[i-5]也是一种状态且有两种方法。我们不妨推测

dp[i]=dp[i-1]+dp[i-2]+2*dp[i-3]+2*dp[i-4]+……+2*dp[1],则

dp[i-1]=dp[i-2]+dp[i-3]+2*dp[i-4]+……+2*dp[1];

两式想减即可得到递推式:dp[i]=2*dp[i-1]+dp[i-3].

接下来只要初始化一下dp[1],dp[2],dp[3]即可。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const int N = 10000010;
const ll M = 1000000007;
int dp[N];
int n;
int main()
{
    cin >> n;
    dp[1] = 1;
    dp[2] = 2;
    dp[3] = 5;
    for (int i = 4; i <= n; i++)
    {
        dp[i] = ((dp[i - 1] * 2)%M + dp[i - 3]%M)%M;
    }
    cout << dp[n] << endl;
    return 0;
}

扫雷

题目链接P2038 - [蓝桥杯2022初赛] 扫雷 - New Online Judge (ecustacm.cn)

思路:dfs+二分

首先扫雷的操作很符合dfs的应用场景,如果不用二分确定能扫到的雷的范围的话,时间复杂度会达到O(n^2)会超时。我们想一下如果地雷一(x1,y1,r1)爆炸,地雷二(x2,y2,r2) |x1-x2|>r1则必不可能被引爆,所以可引爆的地雷的范围是x2-r<=x1<=x2+r,我们先按照x的大小对一个结构体进行排序,然后dfs,再用二分查找两个边界值,即可稍稍的优化一下。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const int N = 500010;
int n, m,ans;
double dis(double x1, double y1, double x2, double y2)//判断地雷是不是在爆炸范围内
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
struct dilei
{
    int x, y, r;
    int used = 0;//标记地雷是不是已经炸过
}a[N];
struct
{
    int x, y, r;
}b[N];
bool cmp(dilei a, dilei b)
{
    if (a.x < b.x)return true;
    else return false;
}
void dfs(int x,int y,int r)
{
    int mid1 = 0;
    int l = 1, rr = n;
    while (l < rr)
    {
        mid1 = (l + rr) >>1;
        if (a[mid1].x >= x-r)rr = mid1;
        else l = mid1 + 1;
    }
    mid1 = l;
    int mid2 = 0;
    l = 1, rr = n;
    while (l < rr)
    {
        mid2 = (l + rr+1) >> 1;
        if (a[mid2].x <= x+r)l = mid2;
        else rr = mid2 - 1;
    }
    mid2 = l;
    for (int i = mid1; i <= mid2; i++)
    {
        if (!a[i].used && dis(x, y, a[i].x, a[i].y) <= r)
        {
            a[i].used = 1, ans++;
            dfs(a[i].x, a[i].y, a[i].r);
        }
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++)scanf("%lld %lld %lld", &a[i].x, &a[i].y, &a[i].r);
    for (int i = 1; i <= m; i++)scanf("%lld %lld %lld", &b[i].x, &b[i].y, &b[i].r);
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1; i <= m; i++)dfs(b[i].x, b[i].y, b[i].r);
    printf("%d", ans);
    return 0;
}

李白打酒加强版

题目链接P2039 - [蓝桥杯2022初赛] 李白打酒加强版 - New Online Judge (ecustacm.cn)

思路:qaq呜呜呜第一次用dfs结果超时了这里是代码。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 210;
int path[N];
typedef long long ll;
int t, n, m;
ll ans, jiu;
void dfs(int x,int a,int b)
{
    if (jiu < 0)return;
    if (a< 0 || b < 0)return;
    if (x == n + m)
    {
        if (jiu == 1)ans++;
        return;
    }
    for (int i = 0; i < 2; i++)
    {
        path[x] = i;
        if (i == 0)
        {
            jiu--;
            b--;
        }
        if (i == 1)
        {
            jiu *= 2;
            a--;
        }
        dfs(x + 1,a,b);
        if (i == 0)
        {
            jiu++;
            b++;
        }
        if (i == 1)
        {
            jiu /= 2;
            a++;
        }
    }
}
int main()
{
    scanf("%d",&t);
    while (t--)
    {
        ans = 0, jiu = 2;
        scanf("%d %d",&n,&m);
        dfs(1,n, m - 1);
        printf("%lld", ans % 1000000007);
    }
    return 0;
}

嗯然后查了一下要用动态规划写。定义一个数组dp[i][j][k],i是遇到了几家店,j是遇到了几次花,k是还有多少酒。

我们可以知道转移方程:

dp[i][j][k]=dp[i][j][k]+dp[i-1][j][k/2](i&&k%2==0)

dp[i][j][k]=dp[i][j][k]+dp[i][j-1][k+1]

最后输出dp[i][j-1][1]即可。

但是为什么不输出dp[i][j][0]呢?

因为题目中提到,最后一次肯定是遇到了花,但是dp[i][j][0]=dp[i][j-1][1]+dp[i-1][j][0]。很明显这一项dp[i-1][j][0]是指最后一次遇到了店,所以不正确。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 110;
int dp[N][N][N];
int t,n,m;
int main()
{
    cin >> t;
    while (t--)
    {
        memset(dp, 0, sizeof(dp));
        cin >> n >> m;
        dp[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++)
                {
                    if (i > 0 && k % 2 == 0)dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k / 2])%mod;
                    if (j > 0)dp[i][j][k] = (dp[i][j][k] + dp[i][j - 1][k + 1])%mod;
                }
        cout << dp[n][m-1][1]%mod << endl;
    }
    return 0;
}

砍竹子

题目链接P2040 - [蓝桥杯2022初赛] 砍竹子 - New Online Judge (ecustacm.cn)

思路:首先我们可以知道先砍最长的那一类竹子肯定是最优的选择。我们可以定义一个结构体,里面存柱子的长度和编号,在内部进行运算符重载按照高度降序排列,如果高度相同,则按编号升序排列(代码中会讲为什么要这样做)。再将结构体放入一个优先队列中即可。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
int n,b,ans;
ll a;
struct node
{
    ll h ;
    int num;
    bool operator<(const node& x) const
    {
        if (h == x.h)return num > x.num;
        else return h < x.h;
    }
};
priority_queue<node> hp;
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        ll x = 0;
        scanf("%lld", &x);
        hp.push({ x, i });
    }
    while (hp.top().h != 1)
    {
        a = hp.top().h;//取队头元素高度
        b = hp.top().num-1;//取队头元素编号,-1是为了让队头元素的编号也符合下面的规则
        while (hp.top().h == a && hp.top().num == b+1)
        {
            b++;//因为魔法只能砍相邻的竹子,又由于已经对高度相同的竹子按照编号进行了升序排列,上面的hp.top().num == b+1加上b++的操作可以保证高度相同的相邻竹子被一次砍完
            hp.pop();//弹出队头元素
            hp.push({ (ll)sqrt(a / 2 + 1), b });//新元素入队
        }
        ans++;
    }
    printf("%d", ans);
    return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值