【题目链接】
题目大意:
说有$m$个区间,要求选出不超过$k$个区间,使这些区间覆盖的长度最长,问最长长度是多少。
题解:
所有区间按$R$从小到大排序之后可以进行$dp$。
$dp[i][j]$表示:拿了小于等于$i$个区间,最后一个以坐标小于等于$j$为结尾的最长覆盖长度
假设第$x$个区间作为结尾,那么要分两种情况来考虑:
1.可以是之前的结尾小于第$x$个区间的左端点,这种情况很好解决。
2.也可以是之前区间的结尾在第$x$个区间内部。
第二种情况的话:
不允许在区间内部进行枚举点,否则时间复杂度炸了,可以发现要求的是类似于$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; }