【題目連接】php
題目大意:c++
說有$m$個區間,要求選出不超過$k$個區間,使這些區間覆蓋的長度最長,問最長長度是多少。spa
題解:blog
全部區間按$R$從小到大排序以後能夠進行$dp$。排序
$dp[i][j]$表示:拿了小於等於$i$個區間,最後一個以座標小於等於$j$爲結尾的最長覆蓋長度get
假設第$x$個區間做爲結尾,那麼要分兩種狀況來考慮:it
1.能夠是以前的結尾小於第$x$個區間的左端點,這種狀況很好解決。class
2.也能夠是以前區間的結尾在第$x$個區間內部。sort
第二種狀況的話:di
不容許在區間內部進行枚舉點,不然時間複雜度炸了,能夠發現要求的是相似於$dp[j] + i - j$格式的最大值,也就是$i$加上區間上$dp[j]-j$的最大值,所以能夠用ST表計算區間最大值。
#include <bits/stdc++.h> using namespace std; const int maxn = 2100; int T, n, m, K; struct X { int L, R; int x; }s[maxn]; int dp[maxn][maxn]; int t[maxn * 4]; int rmq[maxn][15]; bool cmp(const X&a, const X&b) { return a.R < b.R; } void ST(int num) { for (int i = 1; i <= n; i++) rmq[i][0] = dp[num][i] - i; for (int j = 1; (1 << j) <= n; j++) { for (int i = 1; i + (1 << j) - 1 <= n; i++) { rmq[i][j] = max(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]); } } } int RMQ(int l, int r) { int k = 0; while ((1 << (k + 1)) <= r - l + 1) k++; return max(rmq[l][k], rmq[r - (1 << k) + 1][k]); } int main() { scanf("%d", &T); int cas = 1; while(T --) { scanf("%d%d%d", &n, &m, &K); for(int i = 1; i <= m; i ++) { scanf("%d%d", &s[i].L, &s[i].R); s[i].x = s[i].R - s[i].L + 1; } sort(s + 1, s + 1 + m, cmp); /* dp[i][j], 拿了 <= i 個,最後一個以座標 <= j 爲結尾 */ int ans = 0; ST(0); for(int i = 1; i <= K; i ++) { int now = 1; while(now <= m && s[now].R < i) now ++; for(int j = i; j <= n; j ++) { dp[i][j] = 0; while(now <= m && s[now].R == j) { dp[i][j] = max(dp[i][j], s[now].x + dp[i - 1][s[now].L - 1]); dp[i][j] = max(dp[i][j], RMQ(s[now].L, s[now].R) + j); now ++; } dp[i][j] = max(dp[i][j - 1], dp[i][j]); ans = max(ans, dp[i][j]); if(ans == n) break; } if(ans == n) break; ST(i); } printf("Case #%d: %d\n", cas ++, ans); } return 0; }