1 条题解
-
0
思路与算法
先按x排序,离散化所有点的高度并加0。设f[h][l][r]为:在点l..r的水平范围内,假设高度不超过h的点已由外层矩形覆盖,覆盖剩余高点的最少额外矩形数。若左或右端点高度<=h,则跳过该端点。否则可在任意k处分成两个独立区间。还可放一个横跨xl..xr的外矩形;令H=floor(S/(xr-xl))(宽0则可取最大点高),若H覆盖两端点,就用1+f[H][l][r]补齐中间更高的点。因为H>=max(yl,yr)>h,依赖高度阈值严格升高,按h从高到低、区间长度从短到长求解。答案是f[0][0][n-1]。
正确性说明
两个落地矩形若水平范围交叉而非包含,重叠部分完全被较高矩形覆盖,可把较低矩形重叠侧截短,不丢失任何覆盖且面积不增。端点只移向已有端点,有限次截短后可得同数或更少矩形、水平范围两两分离或包含的层叠森林;外矩形若不低于内部矩形,则内部冗余可删除。于是最优解可按分离根区间划分,或在某区间用一个低外矩形覆盖低点、递归覆盖内部高点。外矩形的左右边可收缩到它实际负责的最左/最右点,故端点由点坐标决定;固定跨度时取面积允许最高H不会变差,正好对应1+f[H]转移。左右低点已被外层覆盖可免费跳过,区间分割和外层转移因此覆盖所有规范最优森林,且每个转移都给出真实可行矩形组合。DP取最小便是全局最优。矩形可以重叠;没有强加所有矩形互不相交的错误限制。
复杂度
设不同正高度数为H<=n。排序及高度压缩O(n log n),预处理每个水平区间允许最高高度O(n² log n),DP数组清零O(n³)。DP有O(Hn²)状态,每状态最多O(n)分割,因此时间O(n4),空间O(n³);在n<=100下使用unsigned char存最小矩形数(不超过100),数组约1.1MiB。多组输入上述成本对T组求和,已用T10、n100、100个不同高度的数据验证原1秒限制。
易错点
低矩形可以跨过被更高矩形单独覆盖的高点,普通不重叠连续分段DP不成立。面积上限限制的是宽乘高,不是点数。相同x的多点可以用零宽矩形覆盖,不能除以0;它们的最高点被覆盖时同位置低点也被覆盖。S和y均为正,点允许落在矩形边上,面积恰等于S仍合法。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; static unsigned char f[102][102][102]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { int n, S; cin >> n >> S; vector<pair<int, int>> p(n); vector<int> ys(1, 0); for (auto &a : p) { cin >> a.first >> a.second; ys.push_back(a.second); } sort(p.begin(), p.end()); sort(ys.begin(), ys.end()); ys.erase(unique(ys.begin(), ys.end()), ys.end()); int H = ys.size(); memset(f, 0, sizeof(f)); vector<vector<int>> height(n, vector<int>(n)); for (int l = 0; l < n; l++) for (int r = l; r < n; r++) { long long w = p[r].first - p[l].first; long long h = w ? S / w : ys.back(); height[l][r] = upper_bound(ys.begin(), ys.end(), h) - ys.begin() - 1; } for (int h = H - 2; h >= 0; h--) for (int len = 1; len <= n; len++) for (int l = 0; l + len <= n; l++) { int r = l + len - 1; if (p[l].second <= ys[h]) { f[h][l][r] = len == 1 ? 0 : f[h][l + 1][r]; continue; } if (p[r].second <= ys[h]) { f[h][l][r] = len == 1 ? 0 : f[h][l][r - 1]; continue; } int ans = len, j = height[l][r]; if (j > h && p[l].second <= ys[j] && p[r].second <= ys[j]) ans = 1 + f[j][l][r]; if (ans > 1) for (int k = l; k < r; k++) ans = min(ans, (int)f[h][l][k] + f[h][k + 1][r]); f[h][l][r] = ans; } cout << (int)f[0][0][n - 1] << '\n'; } }
- 1