贪心Acwing

/*
选出来尽可能少的点,使得每个区间至少包含一个点
区间问题一般是先排序,要么按左端点排序,要么按右端点排序,要么双关键字排序

1.将每个区间按照右端点从小到大进行排序
2.从前往后枚举每个区间
    如果当前区间已经包含点,pass
    否则说明需要在这个区间新放一个点,放在当前区间的右端点处
*/
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
struct Range
{
    int l, r;
    bool operator< (const Range &W)const
    {
        return r < W.r;//运算符重载,按照右端点排序
    }
}range[N];

int main()
{
    cin>>n;
    for (int i = 0; i < n; i ++ ) cin>>range[i].l>>range[i].r;

    sort(range, range + n);

    int res = 0, ed = -2e9;
//因为最开始一个点都没有选,所以可以把上一个点设为负无穷
    for (int i = 0; i < n; i ++ )//遍历区间
        if (range[i].l > ed)
        {
            res ++ ;
            ed = range[i].r;
        }

    cout<<res<<endl;

    return 0;
}

 

/*
选尽可能多的互不相交的区间,一个区间可以看做一个活动(课程)的开始和结束时间,
有很多个活动(课程),要参加尽可能多的活动(选尽可能多的课程)

这道题和上道题一样也是先将所有区间按照右端点排序
然后从前往后依次枚举每个区间,如果当前区间包含这个点的话,直接pass,
否则选择当前区间的右端点
按照这个方式选择的端点数量就是题中要求的最大互不相交区间数量

为什么最大不相交区间数==最少覆盖区间点数呢?
因为如果几个区间能被同一个点覆盖
说明他们相交了
所以有几个点就是有几个不相交区间
至于为什么是最大的,反证法即可,如果还能找到一个区间互不相交,
则还需要一个点才能覆盖所有区间,此时点数不是最少,矛盾
*/
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
struct Range
{
    int l, r;
    bool operator< (const Range &W)const
    {
        return r < W.r;//运算符重载,按照右端点排序
    }
}range[N];

int main()
{
    cin>>n;
    for (int i = 0; i < n; i ++ ) cin>>range[i].l>>range[i].r;

    sort(range, range + n);

    int res = 0, ed = -2e9;
//因为最开始一个点都没有选,所以可以把上一个点设为负无穷
    for (int i = 0; i < n; i ++ )//遍历区间
        if (range[i].l > ed)
        {
            res ++ ;
            ed = range[i].r;
        }

    cout<<res<<endl;

    return 0;
}

 

/*
首先将所有区间按照左端点从小到大排序
从前往后遍历每个区间,判断当前区间能否放到某个现有的组中
    如果这个区间的左端点比最小组的右端点要小,ranges[i].l<=heap.top() 
    就开一个新组 heap.push(range[i].r)
    如果这个区间的左端点比最小组的右端点要大,则放入该组并更新这一组的右端点
    heap.pop(), heap.push(range[i].r);
*/
#include <iostream>
#include <algorithm>
#include <queue>

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()
{
    cin>>n;
    for (int i = 0; i < n; i ++ ) cin>>range[i].l>>range[i].r;

    sort(range, range + n);

    priority_queue<int, vector<int>, greater<int>> heap;
    //小根堆,堆中放的是已经确定的组的最右的端点
    //heap.top()是对应的最小的最右点
    for (int i = 0; i < n; i ++ )
    {
        auto r = range[i];
//如果当前一个组都没有,或者当前区间的左端点比最小的右端点还要小
//放到任何一组都会有相交部分,新开一组
        if (heap.empty() || heap.top() >= r.l) heap.push(r.r);
        else
        {
            heap.pop();
            heap.push(r.r);
        }
    }

    cout<<heap.size()<<endl;

    return 0;
}

 

/*
选择尽可能少的小区间覆盖大区间

1.将所有区间按照左端点从小到大进行排序
2.从前往后枚举每个区间,在所有能覆盖start的区间中(也就是l<=start),
  选择右端点最大的区间,然后将start更新成右端点的最大值
*/
#include <iostream>
#include <algorithm>

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()
{
    int st, ed;
    cin>>st>>ed;
    cin>>n;
    for (int i = 0; i < n; i ++ ) cin>>range[i].l>>range[i].r;

    sort(range, range + n);

    int res = 0;
    bool success = false;
    for (int i = 0; i < n; i ++ )
    {
        int j = i, r = -2e9;
        while (j < n && range[j].l <= st)
        {//在左端点l<=st的前提下,选右端点尽可能大的小区间
            r = max(r, range[j].r);
            j ++ ;
        }

        if (r < st) break;

        res ++ ;
        if (r >= ed)
        {//初始化为false,只有在最后找到一个区间>=ed时才能设置为true
            success = true;
            break;
        }

        st = r;
        i = j - 1;
    }

    if (!success) res = -1;
    cout<<res<<endl;

    return 0;
}

/*
经典哈夫曼树的模型,每次合并重量最小的两堆果子即可

使用小根堆维护所有果子,每次弹出堆顶的两堆果子,并将其合并,
合并之后将两堆重量之和再次插入小根堆中。
每次操作会将果子的堆数减一,一共操作n−1次即可将所有果子合并成1堆
每次操作涉及到2次堆的删除操作和1次堆的插入操作,计算量是O(logn)
因此总时间复杂度是O(nlogn)
*/
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

int main()
{
    int n;
    cin>>n;

    priority_queue<int, vector<int>, greater<int>> heap;
    while (n -- )
    {
        int x;
        cin>>x;
        heap.push(x);
    }

    int res = 0;
    while (heap.size() > 1)
    {
        int a = heap.top(); heap.pop();
        int b = heap.top(); heap.pop();
        res += a + b;
        heap.push(a + b);
    }

    cout<<res<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值