acwing——贪心区间问题

1.区间问题

1.区间选点

算法思路:
1.先将所有区间以右端点升序排序
如果以左端点升序排序,则会出现
区间a在区间b的上方,所以优先选择a区间的右端点,那么就会导致区间b中不包含任何点。
在这里插入图片描述

2.将定位到的区间右端点和接下来的区间左端点比较,如果找到一个区间左端点比定位区间右端点大,
此时说明两区间中间有空档,将定位移到该区间右端点,直到循环结束。
3.问题等价于求最多多少个不相交的区间的个数。

证明正确性:假定我们的答案是ans,求出来的是cnt,要想证明cnt = ans ,只要证明ans≤并且ans≥cnt。
(1)显然ans≤cnt;
(2)因为自增cnt时的条件是两区间无交集,因此标记这些不相交的区间是标记所有区间的必要条件,所以最终的答案一定要大于等于已经标记的次数,即ans≥cnt。
得证:ans=cnt

#include <iostream>
#include <algorithm>

using namespace std;

typedef pair<int , int> PII;
const int N = 100010;

PII range[N];//first是右端点 second是左端点 

int ans;

int main()
{   
    int n;
    cin >> n;
    
    for(int i = 0 ; i < n ; i ++)
        cin >> range[i]. second >> range[i].first;
        
    sort(range , range + n);
    
    int  r = -2e9;
    for(int i = 0;  i < n ; i++)
    {
        if(range[i].second > r)
        {
            ans++;
            r = range[i].first;
        }
    }
    
    cout << ans << endl;
    
    
    
    return 0;
}

2.最大不相交区间数量

算法思想:代码与区间选点一模一样
证明正确性:
假设最后数量的最大是ans,求得的数量是res。
我们根据上一题求出来res个点,说明最多有res个区间不相交。如果存在ans个区间不相交,且ans>res,则应该选出ans个点来使踏遍每一个区间,但是已经求出res个点足以满足踏遍所有点,说明不存在ans个不相交区间。

代码同上

3.区间分组

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

算法思路:
1.先将区间以左端点升序排序;
2.声明一个小根堆来维护每个组最右边的右端点
3.每次判断新的区间的左端点range[i].first和小根堆中的右端点heap.top(),
如果heap.pop()>=range[i].first,说明不存在一个组能容下新区间,需要超新创建一个组;
否则将新区间放到右端点最小的组,即最右端点是heap.top()的组(只要能容下新区间的组都可以放,这里为了方便直接放到右端点最小的组)。
4.更新新区间加入的/创建的组的右端点
(1)新建组,直接将range[i].second推入堆中;
(2)加入到已有组,将该组的最右端点弹出堆,并将新区间的右端点推入即可。

证明正确性:设最小区间是ans,求出来的区间是cnt
1.显然ans <= cnt
2.当我们开到cnt-1个组合时,若接下来的区间的左端点≤所有组的最右右端点,此时需要再开一个组合,这时range[i].first和cnt个组都有交集(因为是以左端点升序排序,所以不存在新区间的右端点比已有组中某个区间的左端点小的情况),即这cnt个组合有公共点,因此至少需要cnt个组,即ans≥cnt.
得证:ans = cnt

#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;

pair<int , int> range[N];

int main()
{
    cin >> n;
    
    for(int i = 0 ; i < n ; i++)
        cin >> range[i].first >> range[i].second;
        
    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].first) ;
        else    heap.pop();
        
        heap.push(range[i].second);
    }
    
    cout << heap.size() << endl;
    return 0;
}

4.区间覆盖

题目:给定N个闭区间[ai,bi]以及一个线段区间[s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出-1。

算法思路:
1.先以左端点升序排序;
2.起点为st,找到左端点小于等于st的区间,并从中选择右端点最大的区间,用这个右端点更新st;
3.如果找不到左端点小于st的区间,或者循环完所有区间也无法覆盖所给区间,则返回-1。

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

pair<int , int> range[N];

bool success;
int st , ed;
int ans;
int n;

int main()
{
    cin >>st >> ed >> n;
    
    for(int i = 0 ; i < n ; i++)
        cin >> range[i].first >> range[i].second;
        
    sort(range , range + n);
    
    for(int i = 0 ; i < n ; i++)
    {
        int j = i , r = -2e9;//r用来记录右端点最远的点
        while(j < n && range[j].first <= st)
            r = max(r , range[j++].second);
            
        if(r < st)
        {
            ans = -1;
            break;
        }
        
        ans++;
        if(r >= ed)
        {
            success = true;
            break;
        }
        
        st = r;
        i = j  -1;
    }
    
    if(!success) ans = -1;
    cout << ans << endl;
    return 0;
}
发布了14 篇原创文章 · 获赞 3 · 访问量 348

猜你喜欢

转载自blog.csdn.net/Stephen_Zhao0/article/details/105207316