区间分组的解法

简介: 区间分组的解法

看了一下,貌似是求最大”区间厚度的问题。

大家可以把这个问题想象成活动安排问题

有若干个活动,第i个活动开始时间和结束时间是[SiSi,fifi],同一个教室安排的活动之间不能交叠,求要安排所有活动,少需要几个教室?

有时间冲突的活动不能安排在同一间教室,与该问题的限制条件相同,即最小需要的教室个数即为该题答案。

我们可以把所有开始时间和结束时间排序,遇到开始时间就把需要的教室加1,遇到结束时间就把需要的教室减1,在一系列需要的教室个数变化的过程中,峰值就是多同时进行的活动数,也是我们至少需要的教室数。

请大家不要发无意义的评论啦

C++ 代码

#include
#include
using namespace std;
const int N = 100100;
int n;
int b[2 * N], idx;
int main()
{
scanf (“%d”, &n);
for(int i = 0; i < n; i ++)
{
int l, r;
scanf(“%d %d”, &l, &r);
b[idx ++] = l * 2;//标记左端点为偶数。
b[idx ++] = r * 2 + 1;// 标记右端点为奇数。
}
sort(b, b + idx);
int res = 1, t = 0;
for(int i = 0; i < idx; i ++)
{
    if(b[i] % 2 == 0) t ++;
    else t --;
    res = max(res, t);
}
printf ("%d\n", res);
return 0;

}

算法分析

贪心决策

从前往后枚举每个区间,判断此区间能否将其放到现有的组中

如果一个区间的左端点比最小组的右端点要小,ranges[i].l<=heap.top() , 就开一个新组 heap.push(range[i].r);

如果一个区间的左端点比最小组的右端点要大,则放在该组, heap.pop(), heap.push(range[i].r);

每组去除右端点最小的区间,只保留一个右端点较大的区间,这样heap有多少区间,就有多少组。

算法流程

区间分组,在组内区间不相交的前提下,分成尽可能少的组。

而不是尽可能多的组,因为一个区间一组,就是尽可能多组的答案。

等效于把尽可能多的区间塞进同一组,要满足range[i].l > heap.top。

heap 存储的是每个组的最右的端点,由于是小根堆heap.top()是对应的最小的最右点。

那如果遇到,塞不进去的情况呢?

就是heap.top >= range[i].l, 当前区间的左端点比最小的右端点还要小,放到任何一组都会有相交部分。

那就需要新开一组,heap.push(range[i].r).

把所有区间按照左端点从小到大排序

从前往后枚举每个区间,判断此区间能否将其放到现有的组中

heap有多少区间,就有多少组

#include
#include
#include
#include
using namespace std;
const int N = 100010;
int n;
struct Range
{
int l, r;
bool operator< (const Range &W)const
{
return l < W.l;
}
}range[N];
int main()
{
scanf(“%d”, &n);
for (int i = 0; i < n; i ++ )
{
int l, r;
scanf(“%d%d”, &l, &r);
range[i] = {l, r};
}
sort(range, range + n);
priority_queue<int, vector<int>, greater<int>> heap;
for (int i = 0; i < n; i ++ )
{
    if (heap.empty() || heap.top() >= range[i].l){
        heap.push(range[i].r);
    }
    else {
        heap.pop();
        heap.push(range[i].r);
    }
}
printf("%d\n", heap.size());
return 0;

}

Part1:例题

https://www.acwing.com/problem/content/908/

给定 N 个闭区间[ai,bi][ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。

输出最小组数。

Part2:思路

将所有区间按照左端点进行排序

利用小根堆来维护所有组的右端点。

因为判断一个区间q[i]能否放进某个组,条件是li>maxrli>maxr,maxrmaxr指的是组内所有区间中最靠右的右端点,如果这个区间的左端点大于组的右端点,说明没有交集,可以放进这个组里。

所以判断一个区间能否放进之前的组中,条件是li>(maxr)minli>(maxr)min,只要最小的比它小就一定存在,如果最小的都比它大就一定有交集了。所以根本不需要记录组内有哪些区间,只需要把组的最大右端点放进小根堆里,每次用区间的左端点和堆顶元素比较即可。

从前往后遍历每一个区间

首先把第一个区间放进堆里if(heap.empty())

判断当前区间能否放进现有的某个组中

如果不存在这样的组(有交集)p[i].first<=heap.top(),需要新建一个组,直接把区间的右端点放进堆里heap.push(p[i].second)

如果存在这样的组(无交集)p[i].first>heap.top(),可以任意找一个组放进去, 我们每次都找堆顶所在的组,放进去=更新这个组在队中的右端点=heap.top

heap.pop(); heap.push(p[i].second);

(先丢掉原来的右端点,把新的右端点放进去)

Part3:证明

ans:最终答案

cnt:按照给定的贪心算法得到的答案

ans>=cnt

首先cnt一定是合法方案(不一定是最小的),每个组内的区间没有任何交集

ans是所有方案中的最小值,所以ans>=cnt

ans<=cnt

假设枚举到第i个区间时,打算把这个区间放到某个组内,但前cnt-1个组中都有区间与当前区间有交集,说明至少有cnt个区间有公共点lili,所以这cnt个区间一定不会放到同一组中,所以ans>=cnt

Part4:代码

#include
#include
#include
using namespace std;
typedef pair<int,int> PII;
const int N=1e5+10;
PII q[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int l,r;
cin>>l>>r;
q[i]={l,r};
}
sort(q,q+n); //默认按照第一元素(左端点排序)
priority_queue<int,vector,greater>heap;
for(int i=0;i<n;i++)
{
    if(heap.empty()||[i].first<=heap.top())
        heap.push(q[i].second);        
    else
    {
        heap.pop();
        heap.push(q[i].second);
    }
}
cout<<heap.size()<<endl;
return 0;

}


相关文章
|
7月前
|
人工智能 算法 测试技术
【动态规划】【二分查找】C++算法 466 统计重复个数
【动态规划】【二分查找】C++算法 466 统计重复个数
|
7月前
|
算法 测试技术 C++
【动态规划】【离线查询】【前缀和】689. 三个无重叠子数组的最大和
【动态规划】【离线查询】【前缀和】689. 三个无重叠子数组的最大和
|
7月前
|
算法 测试技术 C#
C++二分查找算法:包含每个查询的最小区间
C++二分查找算法:包含每个查询的最小区间
|
7月前
|
C++ Python
leetcode-56:合并区间
leetcode-56:合并区间
61 0
|
2月前
|
C++
Leetcode第56题(合并区间)
这篇文章介绍了LeetCode第56题“合并区间”的解题方法,通过排序和贪心策略合并重叠区间,并提供了C++的代码实现。
26 0
Leetcode第56题(合并区间)
|
4月前
|
算法
LeetCode第56题合并区间
LeetCode第56题"合并区间"的解题方法,通过排序区间并判断重叠后进行合并,有效解决了区间合并问题。
LeetCode第56题合并区间
|
7月前
|
人工智能 BI
经典问题之区间分组
经典问题之区间分组
|
7月前
代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间
代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间
54 0
|
7月前
|
算法 测试技术 C#
二分查找|前缀和|滑动窗口|2302:统计得分小于 K 的子数组数目
二分查找|前缀和|滑动窗口|2302:统计得分小于 K 的子数组数目