Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
一条街的一边有几座房子,因为环保原因居民想要在路边种些树。
路边的地区被分割成块,并被编号成 。每个部分为一个单位尺寸大小并最多可种一棵树。
每个居民都想在门前种些树,并指定了三个号码 。这三个数表示该居民想在地区 和 之间(包括 和 )种至少 棵树。
居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量。
输入格式
输入的第一行是一个整数,代表区域的个数 。
输入的第二行是一个整数,代表房子个数 。
第 到第 行,每行三个整数,第 行的整数依次为 ,代表第 个居民想在 和 之间种至少 棵树。
输出格式
输出一行一个整数,代表最少的树木个数。
输入输出样例
输入
9
4
1 4 2
4 6 2
8 9 2
3 5 2
输出
5
数据规模与约定
对于 100% 的数据,保证:
题目思路详解
用什么方法?
审题时一定要养成一个习惯:看数据规模。
通过数据规模,我们其实就可以大致的推断出这道题怎么求解了。毕竟,出题人出了个 的题,总不能让你用暴力for循环就轻易通关吧,这时我们就要考虑有没有 时间复杂度 的解法了。
比如说这道题,四次方的规模,看上去用 复杂度的解法就能搞定。
想一想有什么解法...动态规划?由于给的请求是以区间的形式存在的,所以不太好搞状态转移....
贪心?
是否可以这样想:要使种的树最少,只要让树尽可能种在区间重叠度高的地方就行了?
怎么个种法?想想贪心的重要方法之一:排序。
我们把所有的区间按照终点从小到大排序。然后我们从左到右对于区间进行填补。我们维护如下的子问题:
排序后,每个区间左边和右边都会有一些重叠区域,我们种到这些重叠区域是最好的。假设我们种好了前 i-1 个区间并到达了第i个区间,这时即使左边有重叠区域,我们肯定也不用再从左边种了,为什么?因为这个子问题的结构就是:种好了前 i-1 个区间,那么即使种在左边的重叠区域,由于左边的区间已经满足要求,所以还是等效于只对当前的区间起到贡献。但是由于后面的区间我们还没有触及,从最右边往左边一个一个种树就可以尽量重在多的重叠区域里,并且对尽量多的区间起到贡献,达到贪心的效果。
再提醒几句
给区间排序时,如果两个区间的终点相同,那起点大的拍左边还是起点小的排左边?其实都一样。各位结合上面的分析仔细想想就明白了。具体而言我是按起点从小到大的。
完整代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cctype>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <numeric>
#define fru(a, b, c) for (int a = b; a <= c; a++)
#define frd(a, b, c) for (int a = b; a >= c; a--)
#define fr(a, b) for (int a = 0; a < b; a++)
#define pb push_back
#define mp make_pair
#define sof sizeof
using namespace std;
using ll = long long;
using db = double;
const int maxn = 30000 + 5;
int arr[maxn];
int ask[maxn][3];
int cmp(const void *a, const void *b)
{
if (*((int *)a + 1) != *((int *)b + 1))
return *((int *)a + 1) - *((int *)b + 1);
else
return *(int *)a - *(int *)b;
}
int main()
{
int n;
cin >> n;
int h;
cin >> h;
for (int i = 0; i < h; i++)
{
int b, e, t;
cin >> b >> e >> t;
ask[i][0] = b;
ask[i][1] = e;
ask[i][2] = t;
}
qsort(ask, h, sizeof(int) * 3, cmp);
for (int i = 0; i < h; i++)
{
int cnt = 0;
for (int j = ask[i][0]; j <= ask[i][1]; j++)
{
if (arr[j])
cnt++;
}
for (int j = ask[i][1]; j >= ask[i][0] && ask[i][2]>cnt ; j--)
{
if (!arr[j])
{
arr[j] = 1;
cnt++;
}
}
}
int res = 0;
for (int i = 0; i <= n; i++)
{
if (arr[i])
res++;
}
cout << res;
}
总结
- 遇到多个区间,可以考虑贪心算法。
- 审题记得看数据规模来推断解法。