bzoj 5185 Lifeguards - 动态规划 - 贪心

题目传送门

  传送点I

  传送点II

题目大意

  给定$n$个区间,问恰好删去其中$k$个,剩下的区间的并的最大总长度。

  显然被包含的区间一定不优。再加上被包含的区间对计数不友好。直接把它删掉。

  注意到题目说恰好,其实是骗人的。多删几个也不会影响(删太多了就有影响了)。

  按照右端点排序,然后用$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 }
posted @ 2018-08-09 20:38  阿波罗2003  阅读(280)  评论(0编辑  收藏  举报