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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy