ZOJ 3953:Intervals(优先队列+思维)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5572
题意:给出n个线段,问最少删除几个线段可以使得任意一个点不会被三个以上的线段覆盖。
思路:首先离散化坐标。
然后想着按右端点从小到大排序后直接O(n)扫的贪心,但是后面发现错误了。
应该按左端点从小到大排序,然后用一个优先队列(按右端点从大到小排序)。
开始扫坐标,当有与该坐标相同的点的左端点就进队,用ed[]记录右端点的位置。
用cnt记录当前这个点有多少个区间覆盖,当cnt>=3的时候就出队更新。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define N 50010 4 struct node { 5 int l, r, id; 6 node () {} 7 node (int l, int r, int id) : l(l), r(r), id(id) {} 8 bool operator < (const node &rhs) const { 9 if(r != rhs.r) return r < rhs.r; 10 return l < rhs.l; 11 } 12 } seg[N]; 13 vector<int> ans; 14 priority_queue<node> que; 15 int ed[N*2], p[N*2]; 16 bool cmp(const node &a, const node &b) { 17 if(a.l != b.l) return a.l < b.l; 18 return a.r < b.r; 19 } 20 int main() { 21 int t; scanf("%d", &t); 22 while(t--) { 23 int n, u, v; scanf("%d", &n); 24 ans.clear(); memset(ed, 0, sizeof(ed)); 25 while(!que.empty()) que.pop(); 26 int cnt = 0, now = 0, num = 0; 27 for(int i = 0; i < n; i++) scanf("%d%d", &u, &v), seg[i] = node(u, v, i + 1), p[cnt++] = u, p[cnt++] = v; 28 sort(seg, seg + n, cmp); sort(p, p + cnt); 29 cnt = unique(p, p + cnt) - p; 30 for(int i = 0; i < n; i++) 31 seg[i].l = lower_bound(p, p + cnt, seg[i].l) - p, 32 seg[i].r = lower_bound(p, p + cnt, seg[i].r) - p; 33 34 for(int i = 0; i < cnt && now < n; i++) { 35 while(seg[now].l == i && now < n) { 36 num++; 37 ed[seg[now].r + 1]++; // 线段的终点 38 que.push(seg[now++]); 39 } 40 num -= ed[i]; // 离开了某线段的范围 41 while(num >= 3) { 42 node tmp = que.top(); que.pop(); 43 num--; ed[tmp.r + 1]--; // 这个线段被删除 44 ans.push_back(tmp.id); 45 } 46 } 47 printf("%d\n", ans.size()); sort(ans.begin(), ans.end()); 48 for(int i = 0; i < ans.size(); i++) printf("%d%c", ans[i], i + 1 == ans.size() ? '\n' : ' '); 49 } 50 return 0; 51 }