📜  画家的分区问题(1)

📅  最后修改于: 2023-12-03 15:40:57.868000             🧑  作者: Mango

画家的分区问题

简介

画家的分区问题是一道计算几何中的问题,其问题描述为:将平面上的 N 个点用 k 条垂直于坐标轴的直线分成 k+1 个区域,使得每个区域内恰好包含一个点,求最小的分区面积和。

解法
贪心算法

总结几种主流的解法,首先是利用贪心算法。假设当前已经画了 i 条线,则前 i - 1 条线已经将所有点分成了 i 个区域,第 i 条线则将这 i 个区域扩展为 i + 1 个区域。那我们肯定希望第 i 条线所造成的面积和是最小的,也就是希望这条线尽可能横跨已有的线段,并且在两个线段之间画出的面积最小。

对于当前已有的 i 条线,我们可以得到 i-1 个区间,设这些区间长度之和为 area,则新添加的直线所产生的新增面积即为 sum - 2*(N-i) * midpoint,其中 sum 为数组中所有点的坐标之和,midpoint 为已有区间的中点坐标之和。因此每次贪心地寻找一条新增的直线即可。

dp 算法

其次是利用 dp 算法,设 f(i,j) 为考虑前 i 个点,分成 j 段的最小面积。则枚举最后一段的起点,即最后一个区间内点的序号,f(i,j) = min{f(k,j-1) + area(k+1,i)}, 其中 area(k+1,i) 表示区间 [k+1,i] 的长度乘以最大的纵坐标跨度。由于有两重循环,时间复杂度为 O(n3)。

二分答案算法

最后是利用二分答案算法,二分最小的分区面积和,然后验证其可行性。判断可行性时,注意到如果一个当前直线段的结果超出了二分的值,需要新增直线段来争取更小的答案。新增的直线段坐标可以利用 lower_bound 和 upper_bound 来快速得到(具体实现方式见下方代码片段)。

代码实现
#include <stdio.h>
#include <algorithm>
struct point { int x, y; } p[105];
int n, k, lim, ans = 2e9;
inline int area(int x1, int y1, int x2, int y2) { return abs(x1-x2)*abs(y1-y2); }
inline int calc(int l, int r, int h) { return area(p[l].x, p[l].y, p[r].x, h); }
inline int solve(int X) {
    int ans = 0, L = p[1].y, R = p[1].y+1;
    for (int i=2;i<=n;i++) {
        L = std::min(L, p[i].y); R = std::max(R, p[i].y+1);
        int len = R-L, tot = 0, last = 1;
        while (last <= n && tot+len <= X) {
            tot += len;
            L = std::min(L, p[last].y);
            R = std::max(R, p[last].y+1);
            last ++;
        }
        ans += calc(last-1, i, R-1);
        if (tot < X) return 2e9;
    }
    return ans;
}
int main() {
    scanf("%d%d", &n, &k);
    for (int i=1;i<=n;i++) scanf("%d%d", &p[i].x, &p[i].y);
    std::sort(p+1, p+1+n, [](point x, point y){ return x.x < y.x; });
    lim = area(p[1].x, p[1].y, p[n].x, p[n].y);
    for (int i=lim/k; i<=lim; i++) ans = std::min(ans, solve(i));
    printf("%d\n", ans);
    return 0;
}

本代码实现采用的是二分答案的方法,通过二分最小答案后,利用贪心思想尽可能合并线段,若最终的分区面积和不超过二分出的值,则说明当前值可行,将右端点设为当前值。若分区面积和超出二分出的值,则需要新增直线段来争取更小的答案。新增直线段坐标可利用 lower_bound 和 upper_bound 来快速得到,从而将总时间复杂度降至 O(n2logS),其中 S 表示总面积。