题目
区间合并(注意边界),贪心
思路
贪心的区间合并
因为是区间合并,所以我们先将区间从小到大排序,以方便合并,排序的时候还是按照区间左端点从小到大排序就行了。排好序之后我们得到类似下面这样的区间段:
由于只是按左端点从小到大排序,所以当左端点相同时,右端点不一定会有序
怎么贪心呢?
首先先找第一个“最优”的区间 a a a,即左端点最小时右端点最大的区间,并记录右端点的值 r r r,然后遍历 a a a 之后的区间,如果可以与 a a a 接上,则寻找这些能与 a a a 接上的所有区间中的最优区间(即右端点最大),然后更新 r r r 的值和答案值。
怎么寻找第一个最优区间?
可以知道的是,仅仅按左端点从小到大排序后,第一个区间并不一定是第一个最优区间,如上图(第二个才是),所以还得单独寻找第一个最优区间,这样代码显得臃肿。所以不如直接按“如果左端点不同,则按左端点从小到大排序,如果左端点相同,则按右端点从大到小排序”,这样的话,排好序之后的第一个区间一定是第一个最优区间。
在最后,如果所有区间都遍历完了,就看此时的 r r r 满不满足大于等于题目给定的 n n n,如果满足则说明应该返回答案,如果不满足则返回 − 1 -1 −1。此外,在排好序之后还应该检查一下第一个区间左端点(即区间左端点的最小值)满不满足小于等于 1 1 1 的条件,如果最小的区间左端点都大于 1 1 1 了,那么说明再怎么也不能满足题意,直接返回 − 1 -1 −1,就不用再进行后续操作了。
代码
#include <stdio.h>
#include <stdlib.h>
/**
* @brief 自定义排序,以左端点从小到大排序,如果左端点相同则右端点从大到小排序
*
* @param a
* @param b
* @return int
*/
int cmp(const void* a, const void* b) {
int(*aa)[2] = (int(*)[2])a;
int(*bb)[2] = (int(*)[2])b;
if ((*aa)[0] == (*bb)[0]) return (*bb)[1] - (*aa)[1];
return (*aa)[0] - (*bb)[0];
}
int main(void) {
int n = 0, m = 0;
scanf("%d%d", &n, &m);
int a[m][2], i = 0;
for (; i < m; i++) {
scanf("%d%d", &a[i][0], &a[i][1]);
}
qsort(a, m, sizeof(int[2]), cmp);
// 注意题目的测试用例似乎没有包含最小的 L 大于 1
// 的情况,所以下面这个判断可以不加,之后加不加就不知道了
if (a[0][0] > 1) {
printf("-1\n");
return 0;
}
// 根据排序规则,我们可以知道排好序之后的第一个区间是最优的
int ans = 1, r = a[0][1], t = r;
i = 1;
// 然后遍历其余区间
while (i < m) {
// 假设已经完成拼接或者已经不合法,则退出循环
if (r == n || a[i][0] > r + 1) break;
// 寻找下一段中最长的区间
while (i < m && a[i][0] <= r + 1) {
if (a[i][1] > t) t = a[i][1];
i++;
}
// 更新区间右端点和答案
r = t;
ans++;
}
// 最后的答案根据最终拼接的区间右端点的大小确定
printf("%d\n", r < n ? -1 : ans);
return 0;
}
标签:return,华华,int,NC23036,端点,区间,最优,排序,唱歌
From: https://blog.csdn.net/m0_52319522/article/details/137120142