[NOIP2005]校门外的树

校门外的树

https://ac.nowcoder.com/acm/problem/16649

某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

该题妥妥的枚举题,枚举优化只要在于减少枚举次数。
对于不同的数据范围,采用不同的求解思路。
先看看数据范围:L(1 <= L <= 10000)和 M(1 <= M <= 100)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 方法1:暴力枚举

数据不算太大,枚举即可。
有一个优化方法--合理的维护数据。代码如下:
#include <bits/stdc++.h>
using namespace std;
int arr[10005] = { 0 };
int L,M,i,j;
int main()
{
    scanf("%d%d",&L,&M);
    for (i = 0;i < M;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        for (j = x;j <= y;j++)
            if (arr[j] == 0)
            {
                L--;
                arr[j] = 1;
            }
    }
    cout << L + 1;
    
    return 0;
}
写该题的时候有两次WA。
第一次WA,想到了一个更好的数据维护方法,减少了时间复杂度。
第二次WA,有一个启示,主要是尽量减少在for循环中定义变量,直接在全局或者main函数里面定义即可。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 

  • 方法2:差分+前缀和

如果扩大数据范围:1 <= L <= 100000和1 <= M <= 100000;如果使用原来的思路--暴力枚举,时间复杂度过大,会有超时的风险,因此变换思路,减少时间复杂度.
方法二使用差分,将对区间的修改改为对区间端点的修改。
#include <bits/stdc++.h>
using namespace std;
int delta[10005] = { 0 };
int L,M,i,j;
int main()
{
    scanf("%d%d",&L,&M);
    for (i = 0;i < M;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if (x > y)
            swap(x,y);
        delta[x]++;
        delta[y + 1]--;
    }
    int a = 0,cnt = 0;
    for (i = 0;i <= L;i++)
    {
        a += delta[i];
        if (a == 0) cnt++;
    }
    cout << cnt;
    
    return 0;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 方法3:合并区间。

#include <bits/stdc++.h>
using namespace std;
int L,M,i;
struct ty
{
    int x;
    int y;
}a[105];
bool comp(ty a,ty b)
{
    if (a.x < b.x) return 1;
    return 0;
}
int main()
{
    scanf("%d%d",&L,&M);
    for (i = 1;i <= M;i++)
        scanf("%d%d",&a[i].x,&a[i].y);
    sort(a + 1,a + 1 + M,comp);
    int cnt = L + 1;
    int l = a[1].x,r = a[1].y;
    for (i = 2;i <= M;i++)
    {
        if (a[i].x < r)
            r = max(r,a[i].y);
        else
        {
            cnt -= (r - l + 1);
            l = a[i].x;
            r = a[i].y;
        }
    }
    cnt -= (r - l + 1);
    cout << cnt;
    
    return 0;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  • 方法4:差分+前缀和+离散化(扫描线操作)

如果继续扩大范围:1 <= L <= 10^9和1 <= M <= 100000怎么办呢?、
使用差分-前缀和加上离散化思想,将方法二的差分方法进一步优化。
#include <bits/stdc++.h>
using namespace std;
int l,m;
struct ty
{
    int pos; //位置,将数轴离散化,减少空间复杂度
    int num; //差分数组
}delta[210] = { 0 };
bool comp(ty a,ty b)
{
    if (a.pos == b.pos) return a.num < b.num;
    return a.pos < b.pos;
}
int main()
{
    scanf("%d%d",&l,&m); //L--马路的长度 M--挖树的次数
    for (int i = 1;i <= m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        delta[i].pos = x;
        delta[i].num = 1;
        delta[i + m].pos = y + 1;
        delta[i + m].num = -1;
    }
    sort(delta + 1,delta + (2 * m) + 1,comp); //按位置在数轴上排序(从小到大)
    int cnt = 0; //记录次数
    int a = 0;
    for (int i = 1;i <= 2 * m;i++)
    {
        a += delta[i].num;
        if (a == 1 && delta[i].num == 1)
            cnt += delta[i].pos - delta[i - 1].pos;
    }
    cnt += l - delta[2 * m].pos + 1;
    cout << cnt;
    
    return 0;
}
全部评论
想问一下..就是数组开在全局变量里面和开在main函数里面?除了咱们函数的使用范围不一样的话,其他还有什么不同吗?我照您的方法二去想,一开始数组开在main里面发现是过不去的?
点赞 回复 分享
发布于 2024-12-29 17:27 江苏

相关推荐

喜欢喜欢喜欢:这是我见过最长最臭的简历
点赞 评论 收藏
分享
05-26 09:07
已编辑
门头沟学院 Java
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务