校门外的树(差分/贪心)

校门外的树 (nowcoder.com)

题目描述

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

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

输入描述:

第一行有两个整数:L(1 <= L <= 10000)和 M(1 <= M <= 100)L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。
接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。

输出描述:

包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。

示例1

输入

500 3
150 300
100 200
470 471

输出

298

思路一:

直接暴力,tree数组为0表示树还在,对每一次操作打上标记

#include<bits/stdc++.h>
#define ll long long
using namespace std;
bool tree[10005];
int main(){
    int a,b,m,l;
    cin>>l>>m;
    while(m--){
        cin>>a>>b;
        for(int i=a;i<=b;i++)tree[i]=1;
    }
    int ans=0;
    for(int i=0;i<=l;i++){
        if(!tree[i])ans++;
    }
    cout<<ans;
    return 0;
}

思路二:

如果数据范围太大,显然会T掉,考虑优化,由于每次移走一段区间,相当于对区间进行操作

若用a[i]表示区间被操作的次数,开delta为差分数组,delta[i]=a[i]-a[i-1],每次区间操作改变差分数组delta[l]和delta[r+1]两个值,大大减少复杂度

AC代码:

#include<iostream>
using namespace std;
int delta[1000001];     //记录相邻两颗树被操作次数的差值
int main(){
    int m,l;
    cin>>l>>m;
    while(m--){
        int l,r;
        cin>>l>>r;
        delta[l]++;     //区间[l,r]内操作次数均加一,即delta[l]++,delta[y+1]--
        delta[r+1]--;
    }
    int ans=0;
    if(delta[0]==0)ans++;
    for(int i=1;i<=l;i++){
        delta[i]+=delta[i-1];   //此时的delta[i]表示操作次数,差分逆运算求原数组
        if(delta[i]==0)ans++;   //操作次数为0,即树还在
    }
    cout<<ans;
    return 0;
}

思路三:

若马路太长,空间不足以开下数组来储存每一棵树的情况,我们应该考虑换个思路

我们发现每次拔去一个区间的树,我们可以将操作过的区间进行合并,合并后区间的长度和即为拔去树的个数,将区间按左端点排序,利用贪心算法即可实现区间合并,维护住合并的区间即可求解所有区间的交集长度。

AC代码:

#include<bits/stdc++.h>
using namespace std;
struct node{
    int l,r;
}q[10005];  //操作的区间
bool cmp(node a,node b){return a.l<b.l;}    //左端点排序
int main(){
    int m,L;
    cin>>L>>m;
    for(int i=1;i<=m;i++){
        cin>>q[i].l>>q[i].r;
    }
    sort(q+1,q+m+1,cmp);
    int l=q[1].l,r=q[1].r;
    int ans=L+1;        //所有树的个数
    for(int i=2;i<=m;i++){      
        if(r>=q[i].l){      //前一个区间右端大于后一个区间的左端,即区间交叉,更新右端点
            r=max(r,q[i].r);
            continue;
        }       
        else{
            ans-=(r-l+1);       //区间不交叉,当前区间长度即为拔去的树个数
            l=q[i].l,r=q[i].r;      //维护的区间更新为下一区间
        }
    }
    cout<<ans-(r-l+1);      //最后减去最后一个区间的树
    return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Auroraaaaaaaaaaaaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值