3.13蓝桥杯基础五题

今天完成了蓝桥杯中二分和前缀和问题的训练

  • 第一题

机器人跳跃问题

机器人向前跳增加二倍能量减少下一层能量

二分算法能够解决包括但不限于单调递增的问题

故我们寻找最小需要的能量值

创建check方法 如果通过返回true 继续进行二分查找 不通过的话则将左区间定为mid+1

最后输出 右端点即为结果

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n;
int h[N];


bool check(int e){
    for(int i = 0; i < n; i ++ ){
        e = e*2 - h[i];
        if(e < 0) return false;
        if(e > 1e5) return true;
    }
    return true;
}

int main(){
    scanf("%d",&n);
    for(int i = 0; i < n; i++) scanf("%d",&h[i]);
    
    int l = 0, r = 1e5;
    while(l < r){
        int mid = l + r >> 1;
        if(check(mid)) r = mid;
        else l = mid + 1;
    }
    
    printf("%d\n",r);
    
    return 0;
}

  • 第二题

四平方和

一个数化为四个数的平方

这个题由于对于时间有所限制 暴力通过四重循环无法解决

选择用空间换时间进行解决

先进行二重循环 将每一次循环的值和 两数平方的和存储

再进行二重循环 将剩下两个数的平方和存储

学习到了在循环体内定义新的operation<方法

由于循环体要进行sort排序 我们定义循环的优先级

bool operator< (const Sum &t)const
    {
        if (s != t.s) return s < t.s;
        if (c != t.c) return c < t.c;
        return d < t.d;
    }
//这里优先级 s > c > d

代码如下:

/*
一个数字求四个数字的平方和
*/

#include <bits/stdc++.h>

using namespace std;

const int N = 2500010;

struct Sum
{
    int s, c, d;
    bool operator< (const Sum &t)const
    {
        if (s != t.s) return s < t.s;
        if (c != t.c) return c < t.c;
        return d < t.d;
    }
}sum[N];

int n, m;

int main()
{
    cin >> n;

    for (int c = 0; c * c <= n; c ++ )
        for (int d = c; c * c + d * d <= n; d ++ )
            sum[m ++ ] = {c * c + d * d, c, d};

    sort(sum, sum + m);

    for (int a = 0; a * a <= n; a ++ )
        for (int b = 0; a * a + b * b <= n; b ++ )
        {
            int t = n - a * a - b * b;
            int l = 0, r = m - 1;
            while (l < r)
            {
                int mid = l + r >> 1;
                if (sum[mid].s >= t) r = mid;
                else l = mid + 1;
            }
            if (sum[l].s == t)
            {
                printf("%d %d %d %d\n", a, b, sum[l].c, sum[l].d);
                return 0;
            }
        }

    return 0;
}

第三个题目

分巧克力

这个题目比较简单 将n块巧克力分给n个小朋友 其中给出巧克力尺寸和块数目

求出最大每个小朋友能得到边长为多少的正方形巧克力

单调区间内问题 使用二分的方法进行遍历

对每一个边长进行遍历 创建check方法 如果可以就返回true 不可以就返回false

/*
分巧克力问题 ,能用数组解决尽量用数组解决。。
*/


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

using namespace std;

const int N = 100010;

int n, k;
int h[N], w[N];

bool check(int mid)
{
    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        res += (h[i] / mid) * (w[i] / mid);
        if (res >= k) return true;
    }

    return false;
}

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

    int l = 1, r = 1e5;
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }

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

    return 0;
}

发现自己对结构体的使用不熟练。

  • 第四题

激光炸弹

题目要求尽可能多的炸到边长要求内的最高分数

我们选择使用前缀和遍历的方法

/*
激光炸弹问题
前缀和问题
处理前缀和时将边界都+1 避免边界问题
*/

#include <bits/stdc++.h>

using namespace std;

const int N = 5010;

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

int main(){
    int cnt, R;
    cin >> cnt >> R;
    R = min(5001,R);
    n = m = R;

    while(cnt -- ){
        int x, y, w;
        cin >> x >> y >> w;
        x ++, y ++;
        n = max(x, n);
        m = max(y, m);
        s[x][y] += w;
    }

    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];
        }
    }
    int ret = 0;
    for(int i = R; i <= n ;i ++ ){
        for(int j = R; j<= m; j ++){
            ret = max(ret, s[i][j] - s[i-R][j] - s[i][j-R] + s[i-R][j-R]);
        }
    }
    cout << ret << endl;
    return 0;
}

先计算出前缀和数组 然后对前缀和数组进行边长为R的 矩阵计算 计算中有很多小细节需要注意。

  • 第五题

K倍区间

要求求一个数组 有多少个区间是 某个数的k倍数

这个题的思想层层递进

首先我们想到了暴力的方法

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int a[N],s[N];

int main(){
    int n,k;
    cin >> n >> k;
    for(int i = 1; i <= n; i ++ ) scanf("%d",&a[i]);
    s[1] = a[1];
    s[0] = a[0] = 0;
    for(int i = 2; i <= n; i ++ ) s[i] = a[i] + s[i-1];
    //准备好了前缀和数组
    int ret = 0, target = 0;
    for(int i = 1; i <= n; i ++ ){
        for(int j = i;j <= n; j ++ ){
            target = s[j] - s[j-i];
            if(target > 1 && (target%k) == 0) ret ++ ;
        }
    }
    cout << ret << endl;
    return 0;
}

这个方法中我们先计算出了前缀和数组 然后对前缀和数组进行了双重遍历

遍历每一种区间长度的 全部区间 如果成立 就记录加一

进阶算法:由于前缀和数组的特性 如果数组的前缀和R 和 L 对于某个值K的模是相同的 ,那么这个区间 是值K的倍区间 因为他们有相同的余数 相减后 刚好得到倍K

由此思想 我们可以先建立一个数组cnt 来存储每一个前缀和关于数K的余数

在计算的过程中遍历

    cnt[0] = 1;
    for(int i = 1; i <= n; i ++ ){
        ret += cnt[s[i] % k];
        cnt[s[i] % k] ++ ;
    }
/*
首先cnt[0] = 1 是因为如果后面的数有余数为0的情况时 和前面所有的数之和就是K倍区间
然后进行一轮一轮的运算 每走一步统计前面的cnt数组情况
再经过回顾 将ret进行计数

秒哇!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值