算法刷题day01

目录

  • 引言
  • 一、数的范围
  • 二、数的三次方根
  • 三、机器人跳跃问题
  • 四、四平方和
  • 五、分巧克力问题
  • 六、总结

引言

搞这个算法刷题系列主要是为了备战蓝桥杯而准备的,目前处于还正在看一些算法基础课程,要先把一些理论知识先给搞懂,然后写题的时候才能有套路的去解。
为什么要写这个系列?

  • 由于深知学算法竞赛最重要的是不断地刷题总结,所以为了避免自己陷入误区,所以开始一边刷题,先刷之前已经学过的类型的题,两个拳头打人,才能事半功倍。
  • 同时因为怕自己没有自律,所以开始在博客上发表文章,我觉得这既可以给自己一个督促(心里觉得广大网友有种隐形的督促感),又同时给自己一种成就感,所以我觉得这种形式很好。
  • 还有就是学算法实际上就是毅力和记忆力的比拼,说实话就是记忆,这个算法题你不会,并不是因为你蠢、笨,而是因为自己还不知道,自己没刷过这种类型的题目,实际上就是这样的,所以总结,写题解,自言自语的这个过程是很重要的,一道题忘了,看一下就想起来了。
  • 最重要的一点就是理工类科目,往往是一道题,看一下别人写的,或者有种思路就觉得自己已经会了,实际上到了考试、比赛的时候,自己往往是做不出来的,今天做题的时候深有体会,往往那些细小的细节是能够决定这道题的成败的,而这些在考试、比赛场上会影响到自己心态、自信心等等的,牵一发而动全身,所以一道题一定要自己尝试动手写一遍,想办法找出问题,为什么思路对但是就是不能AC,所以刷题必须要自己花时间去多练、多思考、多总结!

一、数的范围

标签:二分

题目描述:

给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1。

输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1∼10000范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。

输出格式共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。

数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000

输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1

示例代码:

#include 
#include 

using namespace std;

const int N = 100010;

int n, q;
int a[N];

int main()
{
    scanf("%d%d", &n, &q);
    
    for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
    
    while(q--)
    {
        int k;
        scanf("%d", &k);
        
        int res1 = -1, res2 = -1;
        int l = 0, r = n - 1;
        while(l < r)
        {
            int mid = l + r >> 1;
            if(a[mid] >= k) r = mid;
            else l = mid + 1;
        }
        
        if(a[l] == k) res1 = l;
        
        l = 0, r = n - 1;
        while(l < r)
        {
            int mid = l + r + 1 >> 1;
            if(a[mid] <= k) l = mid;
            else r = mid - 1;
        }
        
        if(a[l] == k) res2 = l;
        
        printf("%d %d\n", res1, res2);
    }
    
    return 0;
}

二、数的三次方根

标签:二分

题目描述:

给定一个浮点数 n,求它的三次方根。

输入格式
共一行,包含一个浮点数 n。

输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 6 位小数。

数据范围
−10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000

示例代码:

#include 
#include 

using namespace std;

int main()
{
    double n;
    cin >> n;
    
    double l = -1e4, r = 1e4, mid;
    while(r - l > 1e-8)
    {
        mid = l + (r - l) / 2;
        if(mid * mid * mid > n) r = mid;
        else l = mid;
    }
    
    printf("%.6f\n", mid);
    
    return 0;
}

三、机器人跳跃问题

标签:二分

题目描述:

机器人正在玩一个古老的基于 DOS 的游戏。

游戏中有 N+1 座建筑——从 0 到 N 编号,从左到右排列。

编号为 0 的建筑高度为 0 个单位,编号为 i 的建筑高度为 H(i) 个单位。

起初,机器人在编号为 0 的建筑处。

每一步,它跳到下一个(右边)建筑。

假设机器人在第 k 个建筑,且它现在的能量值是 E,下一步它将跳到第 k+1 个建筑。

如果 H(k+1)>E,那么机器人就失去 H(k+1)−E 的能量值,否则它将得到 E−H(k+1) 的能量值。

游戏目标是到达第 N 个建筑,在这个过程中能量值不能为负数个单位。

现在的问题是机器人至少以多少能量值开始游戏,才可以保证成功完成游戏?

输入格式
第一行输入整数 N。
第二行是 N 个空格分隔的整数,H(1),H(2),…,H(N) 代表建筑物的高度。

输出格式
输出一个整数,表示所需的最少单位的初始能量值上取整后的结果。

数据范围
1≤N,H(i)≤105,
输入样例1:
5
3 4 3 2 4
输出样例1:
4
输入样例2:
3
4 4 4
输出样例2:
4
输入样例3:
3
1 6 4
输出样例3:
3

示例代码:

#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e5+10;

int n;
int h[N];

bool check(int mid)
{
    LL res = mid;
    for(int i = 1; i <= n; ++i)
    {
        res = res * 2 - h[i];
        if(res > 1e5) return true;  //res会爆long long的,所以要加这个判断
        if(res < 0) return false;
    }
    
    return true;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; 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", l);
    
    return 0;
}

四、四平方和

标签:二分

题目描述:

四平方和定理,又称为拉格朗日定理:

每个正整数都可以表示为至多 4 个正整数的平方和。

如果把 0 包括进去,就正好可以表示为 4 个数的平方和。

比如:
5=02+02+12+22
7=12+12+12+22

对于一个给定的正整数,可能存在多种平方和的表示法。

要求你对 4 个数排序:

0≤a≤b≤c≤d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法。

输入格式
输入一个正整数 N。

输出格式
输出4个非负整数,按从小到大排序,中间用空格分开。

数据范围
0

示例代码:

#include 
#include 
#include 
#include 

using namespace std;

const int N = 5e6+10;

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

int cnt;

int main()
{
    int n;
    cin >> n;
    
    for(int i = 0; i * i <= n; ++i)
    {
        for(int j = i; i * i + j * j <= n; ++j)
        {
            int s = i * i + j * j;
            sum[cnt++] = {s,i,j};
        }
    }
    
    sort(sum,sum+cnt);
    
    //for(int i = 0; i < cnt; ++i)  这样就是按sum的值,而不是a和b的大小了,虽然最后也正确但不是最佳的
    for(int i = 0; i * i <= n; ++i)  //这样遍历才是按联合主键升序,拿cnt遍历的话,答案也对,不过就不是按联合主键排了,是按sum排
    {
        for(int j = i; i * i + j * j <= n; ++j)
        {
            int key = n - i * i - j * j;
            
            int l = 0, r = cnt - 1;
            while(l < r)
            {
                int mid = l + r >> 1;
                if(sum[mid].s >= key) r = mid;
                else l = mid + 1;
            }
            
            if(sum[l].s == key)
            {
                int c = sum[l].a, d = sum[l].b;
                printf("%d %d %d %d\n",i, j, c, d);
                return 0;
            }
        }
    }
    
    return 0;
}

五、分巧克力问题

标签:二分

题目描述:

儿童节那天有 K 位小朋友到小明家做客。

小明拿出了珍藏的巧克力招待小朋友们。

小明一共有 N 块巧克力,其中第 i 块是 Hi×Wi 的方格组成的长方形。

为了公平起见,小明需要从这 N 块巧克力中切出 K块巧克力分给小朋友们。

切出的巧克力需要满足:
形状是正方形,边长是整数大小相同例如一块 6×5 的巧克力可以切出 6 块 2×2 的巧克力或者 2 块 3×3 的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

输入格式
第一行包含两个整数 N 和 K。

以下 N 行每行包含两个整数 Hi 和 Wi。

输入保证每位小朋友至少能获得一块 1×1 的巧克力。

输出格式
输出切出的正方形巧克力最大可能的边长。

数据范围
1≤N,K≤105
,
1≤Hi,Wi≤105
 
输入样例:
2 10
6 5
5 6
输出样例:
2

示例代码:

#include 
#include 

using namespace std;

typedef long long LL;

const int N = 1e5+10;

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

bool check(int a)
{
    LL res = 0;
    for(int i = 0; i < n; ++i)
    {
        res += ((LL)h[i] / a) * (w[i] / a);
        if(res >= k) return true;  //必须在这里判断一下,不然long long也不够用
    }
    
    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", l);
    
    return 0;
}

六、总结

  • 对于二分模板,记住那个if(check()) l = mid 这一行一定是r = mid 或者l = mid,然后才能判断这样是否为l = mid才能+1
  • 实数二分没有<=,只有<,>
  • 有些数要判断是否会爆int,这个必须得考虑,然后有些会爆long long的,所以有些得加个判断break出去

你可能感兴趣的:(#,算法刷题,算法)