bzoj 5185 Lifeguards - 动态规划 - 贪心
显然被包含的区间一定不优。再加上被包含的区间对计数不友好。直接把它删掉。
注意到题目说恰好,其实是骗人的。多删几个也不会影响(删太多了就有影响了)。
按照右端点排序,然后用$f[i][j]$表示考虑前$i$个区间,已经选了删掉$j$个。
考虑转移,第一种情况是第$i$个区间不选,直接通过$i - 1$转移。
第二种情况是要选$i$。然后要计算贡献。这时考虑上一个选择的是什么?
- 如果和第$i$个区间有相交,那么显然选择左端点最小的一个和当前区间相交的线段作为上一个。然后中间的线段全部删掉(因为它们被包含了。)。答案补上这个区间覆盖不到的但当前区间能够覆盖的地方。(如果上一个不是选的这个区间也无妨,因为这样一定没有下面这样的转移优)
- 否则从左端点最小的一个区间的前一个区间转移。答案加上当前区间长度。
Code
1 /** 2 * bzoj 3 * Problem#5185 4 * Accepted 5 * Time: 1140ms 6 * Memory: 43876k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 typedef class Segment { 13 public: 14 int l, r; 15 16 boolean operator < (Segment b) const { 17 if (r ^ b.r) return r < b.r; 18 return l < b.l; 19 } 20 }Segment; 21 22 const int N = 1e5 + 5, K = 105; 23 24 int n, m, k; 25 Segment* bs; 26 Segment* ss; 27 int f[N][K]; 28 29 inline void init() { 30 scanf("%d%d", &n, &k); 31 bs = new Segment[(n + 1)]; 32 ss = new Segment[(n + 1)]; 33 for (int i = 1; i <= n; i++) 34 scanf("%d%d", &bs[i].l, &bs[i].r); 35 } 36 37 inline void solve() { 38 sort(bs + 1, bs + n + 1); 39 for (int i = 1; i <= n; i++) { 40 if (i == 1 || bs[i].r != bs[i - 1].r) 41 ss[++m] = bs[i]; 42 else 43 k--; 44 } 45 46 int mil = (signed) (~0u >> 1); 47 n = 0; 48 for (int i = m; i; i--) { 49 if (ss[i].l < mil) 50 bs[++n] = ss[i], mil = ss[i].l; 51 else 52 k--; 53 } 54 sort(bs + 1, bs + n + 1); 55 k = max(k, 0); 56 memset(f, 0x80, sizeof(f[0]) * (m + 1)); 57 int p = 1; 58 f[0][0] = 0; 59 for (int i = 1; i <= n; i++) { 60 while (p <= i && bs[p].r < bs[i].l) 61 p++; 62 assert(p <= i); 63 for (int j = 1; j <= k; j++) 64 f[i][j] = max(f[i][j], f[i - 1][j - 1]); 65 f[i][k] = max(f[i][k], f[i - 1][k]); 66 67 for (int j = 0; j <= k; j++) { 68 int cj = min(j + i - p - 1, k); 69 if (p ^ i) 70 f[i][cj] = max(f[i][cj], f[p][j] + bs[i].r - bs[p].r); 71 cj = min(cj + 1, k); 72 f[i][cj] = max(f[i][cj], f[p - 1][j] + bs[i].r - bs[i].l); 73 } 74 } 75 printf("%d\n", f[n][k]); 76 } 77 78 int main() { 79 init(); 80 solve(); 81 return 0; 82 }