繁体   English   中英

从具有最大面积的 n 个子集中找出 k

[英]Find k out of n subset with maximal area

我有n个点,必须找到k个点之间的最大联合区域( k <= n )。 因此,它是这些点面积的总和减去它们之间的公共面积。

在此处输入图像描述 ] 1

假设我们有n=4, k=2 如上图所示,面积是从每个点到原点计算的,最终面积是B面积与D面积之和(只计算它们相交的面积一次)。 没有一点被支配

我已经实现了一个自下而上的动态规划算法,但它在某处有错误。 这是打印出最佳结果的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct point {
    double x, y;
} point;
struct point *point_ptr;

int n, k;
point points_array[1201];
point result_points[1201];

void qsort(void *base, size_t nitems, size_t size,
           int (*compar)(const void *, const void *));

int cmpfunc(const void *a, const void *b) {
    point *order_a = (point *)a;
    point *order_b = (point *)b;
    if (order_a->x > order_b->x) {
        return 1;
    }
    return -1;
}

double max(double a, double b) {
    if (a > b) {
        return a;
    }
    return b;
}

double getSingleArea(point p) {
    return p.x * p.y;
}

double getCommonAreaX(point biggest_x, point new_point) {
    double new_x;
    new_x = new_point.x - biggest_x.x;
    return new_x * new_point.y;
}

double algo() {
    double T[k][n], value;
    int i, j, d;
    for (i = 0; i < n; i++) {
        T[0][i] = getSingleArea(points_array[i]);
    }
    for (j = 0; j < k; j++) {
        T[j][0] = getSingleArea(points_array[0]);
    }
    for (i = 1; i < k; i++) {
        for (j = 1; j < n; j++) {
            for (d = 0; d < j; d++) {
                value = getCommonAreaX(points_array[j - 1], points_array[j]);
                T[i][j] = max(T[i - 1][j], value + T[i - 1][d]);
            }
        }
    }
    return T[k - 1][n - 1];
}

void read_input() {
    int i;
    fscanf(stdin, "%d %d\n", &n, &k);
    for (i = 0; i < n; i++) {
        fscanf(stdin, "%lf %lf\n", &points_array[i].x, &points_array[i].y);
    }
}

int main() {
    read_input();
    qsort(points_array, n, sizeof(point), cmpfunc);
    printf("%.12lf\n", algo());
    return 0;
}

输入:

5 3
0.376508963445 0.437693410334
0.948798695015 0.352125307881
0.176318878234 0.493630156084
0.029394902328 0.951299438575
0.235041868262 0.438197791997

其中第一个数字等于n ,第二个k和以下几行分别是每个点的xy坐标,结果应该是: 0.381410589193

而我的是0.366431740966 所以我错过了一点?

This is a neat little problem, thanks for posting, In the remainder, I'm going to assume no point is dominated , that is, there are no points c such that there exists a point d with c.x < dx and c.y < dy 如果有,那么使用c永远不是最优的(为什么?),所以我们可以放心地忽略任何优势点。 您的示例点都没有被支配。

您的问题展示了最优子结构:一旦我们决定了第一次迭代中要包含的项目,我们再次k - 1n - 1的相同问题(我们从允许的点集中删除所选项目)。 当然,收益取决于我们选择的集合——我们不想计算区域两次。

我建议我们按 x 值按升序对所有点进行预排序。 这确保了选择点的值可以计算为分段区域。 我将用一个例子来说明:假设我们有三个点, (x1, y1), ..., (x3, y3)的值是(2, 3), (3, 1), (4, .5) 那么这些点所覆盖的总面积为(4 - 3) *.5 + (3 - 2) * 1 + (2 - 0) * 3 我希望它在图表中有意义:

数字

根据我们没有支配点的假设,我们总是会有这样一个弱下降的数字。 这样,预排序就解决了“两次计算面积”的整个问题!

让我们把它变成一个动态规划算法。 考虑一组n个点,标记为{p_1, p_2, ..., p_n} d[k][m]为大小为k + 1的子集的最大面积,其中子集中的第(k + 1)个点是点p_m 显然,如果m < k + 1 ,则不能将m选为第(k + 1)个点,因为这样我们就会有一个大小小于k + 1的子集,这永远不是最优的。 我们有以下递归,

d[k][m] = max {d[k - 1][l] + (p_m.x - p_l.x) * p_m.y, for all k <= l < m}.

k = 1的初始情况是每个点的矩形区域。 初始情况连同更新方程足以解决问题。 我估计以下代码为O(n^2 * k) n中的平方项也可能会降低,因为我们有一个有序集合,并且可能能够应用二分搜索在log n时间内找到最佳子集,从而将n^2减少到n log n 我把这个留给你。

在代码中,我尽可能重用了上面的符号。 这有点简洁,但希望通过给出的解释清楚。

#include <stdio.h>

typedef struct point
{
    double x;
    double y;
} point_t;


double maxAreaSubset(point_t const *points, size_t numPoints, size_t subsetSize)
{
    // This should probably be heap allocated in your program.
    double d[subsetSize][numPoints];

    for (size_t m = 0; m != numPoints; ++m)
        d[0][m] = points[m].x * points[m].y;

    for (size_t k = 1; k != subsetSize; ++k)
        for (size_t m = k; m != numPoints; ++m)
            for (size_t l = k - 1; l != m; ++l)
            {
                point_t const curr = points[m];
                point_t const prev = points[l];

                double const area = d[k - 1][l] + (curr.x - prev.x) * curr.y;

                if (area > d[k][m])  // is a better subset
                    d[k][m] = area;
            }

    // The maximum area subset is now one of the subsets on the last row.
    double result = 0.;

    for (size_t m = subsetSize; m != numPoints; ++m)
        if (d[subsetSize - 1][m] > result)
            result = d[subsetSize - 1][m];

    return result;
}

int main()
{
    // I assume these are entered in sorted order, as explained in the answer.
    point_t const points[5] = {
            {0.029394902328, 0.951299438575},
            {0.176318878234, 0.493630156084},
            {0.235041868262, 0.438197791997},
            {0.376508963445, 0.437693410334},
            {0.948798695015, 0.352125307881},
    };

    printf("%f\n", maxAreaSubset(points, 5, 3));
}

使用您提供的示例数据,我根据需要找到了0.381411的最佳结果。

据我所知,您和我都使用相同的方法来计算面积以及整体概念,但我的代码似乎返回了正确的结果。 也许审查它可以帮助您找到差异。

JavaScript 代码:

 function f(pts, k){ // Sort the points by x pts.sort(([a1, b1], [a2, b2]) => a1 - a2); const n = pts.length; let best = 0; // m[k][j] represents the optimal // value if the jth point is chosen // as rightmost for k points let m = new Array(k + 1); // Initialise m for (let i=1; i<=k; i++) m[i] = new Array(n); for (let i=0; i<n; i++) m[1][i] = pts[i][0] * pts[i][1]; // Build the table for (let i=2; i<=k; i++){ for (let j=i-1; j<n; j++){ m[i][j] = 0; for (let jj=j-1; jj>=i-2; jj--){ const area = (pts[j][0] - pts[jj][0]) * pts[j][1]; m[i][j] = Math.max(m[i][j], area + m[i-1][jj]); } best = Math.max(best, m[i][j]); } } return best; } var pts = [ [0.376508963445, 0.437693410334], [0.948798695015, 0.352125307881], [0.176318878234, 0.493630156084], [0.029394902328, 0.951299438575], [0.235041868262, 0.438197791997] ]; var k = 3; console.log(f(pts, k));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM