avatar
fireworks99
keep hungry keep foolish

POJ 2376 Cleaning Shifts(贪心)

Description

有一段从1到n的区间,现有多条线段(左右界不同,有重合区域),求最少需几条线段才能覆盖整个大区间?

Analyze

我知道选择一个区间要尽可能选右界大的,于是我按右界排序,后来发现做不了……这题应该明确:在左界能承接上一个区间的前提下选右界最大的。我一直难以做到左右兼顾,按左界排序?按右界排序?先左后右?先右后左?没有合适的排序方式。后来发现贪心有它的前提和选择。

贪心前提:左界承接前段

贪心选择:右界选择最右

我们得先满足前提,后贪心选择。

那就按左界从小到大排序,保证左边的是满足前提的,while去遍历

遍历过程中用right = max(right, a[pos].r)来贪心选择最右的

更新右界,再从上次停止的位置起重复上述过程

Attention:

上次停止的位置(设为pos)是满足条件的最右的位置,更新后的右界(设为right)是遍历过程中最右的界线,right >= a[pos].r >= a[pos].l,所以这个方法只需要遍历一遍,O(n)

Code

#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

struct node
{
    int l, r;
} a[25005];

bool cmp(node a, node b)
{
    return a.l < b.l;
}

int main()
{
    int n, t;
    scanf("%d%d", &n, &t);
    for(int i = 0; i < n; ++i)
        scanf("%d%d", &a[i].l, &a[i].r);
    sort(a, a + n, cmp);
    int r = 0, pos = 0, ans = 0;
    while(r < t)
    {
        int rmax = -1;
        while(a[pos].l <= r + 1)
        {
            rmax = max(rmax, a[pos].r);
            pos++;
        }
        if(rmax == -1)
        {
            cout << "-1" << '\n';
            return 0;
        }
        ans++;
        r = rmax;
    }
    cout << ans << '\n';
    return 0;
}

///4 10
///1 5
///2 10
///9 10
///3 9
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy