按点覆盖细分

时间:2016-06-21 05:56:26

标签: algorithm greedy

我确实搜索并查看了以下链接,但没有帮助。

  1. Point covering problem
  2. Segments poked (covered) with points - any tricky test cases?
  3. Need effective greedy for covering a line segment
  4. 问题描述:

     You are given a set of segments on a line and your goal is to mark as
     few points on a line as possible so that each segment contains at least
     one marked point
    

    任务。

      Given a set of n segments {[a0,b0],[a1,b1]....[an-1,bn-1]} with integer
      coordinates on a line, find the minimum number 'm' of points such that 
      each segment contains at least one point .That is,  find a set of 
      integers X of the minimum size such that for any segment [ai,bi] there 
      is a point x belongs X such that ai <= x <= bi
    

    输出描述:

      Output the minimum number m of points on the first line and the integer 
    coordinates of m points (separated by spaces) on the second line
    

    示例输入 - 我

    3
    1 3
    2 5
    3 6
    

    输出 - 我

    1  
    3
    

    样本输入 - II

    4
    4 7
    1 3
    2 5
    5 6
    

    输出 - II

    2   
    3 6 
    

    我不明白这个问题本身。我需要解释,如何解决上述问题,但我不想要代码。示例非常有用

3 个答案:

答案 0 :(得分:16)

也许这个问题的表述会更容易理解。您有n个人,每个人都可以容忍不同的温度范围[ai, bi]。您希望找到让他们都满意的最小房间数,即您可以将每个房间设置为特定温度,以便每个人都可以在他/她的温度范围内找到房间。

至于如何解决问题,你说你没有想要代码,所以我只是粗略地描述一种方法。想想你有最冷的房间。如果让它升温一度不会导致任何人再也无法容忍那个房间,那么你可能会增加,因为这样只会让更多人使用那个房间。因此,您应该设置的第一个温度是最热爱的人仍然可以忍受的最温暖的温度。换句话说,它应该是bi中最小的一个。现在这个房间将满足您的人员的一些子集,因此您可以将它们从考虑中删除。然后对剩下的人重复这个过程。

现在,为了有效地实现这一点,你可能不想按字面意思做我上面所说的。我建议首先根据bi对人进行排序,对于i人,尝试使用现有的房间来满足他们。如果您不能,请尝试创建一个具有最高温度的新工具来满足它们,即bi

答案 1 :(得分:1)

是的,描述非常含糊,唯一对我有意义的是:

  1. 你有一些行
  2. 一行上的细分由l,r

    定义

    其中一个参数是距离行首的距离,第二个是段长度。哪一个很难说,因为这些字母对于这种描述并不常见。我的赌注是:

    • l段长度
    • r从行首开始的段(start?)的距离

    segment

  3. 您想找到最小分数

    这样每个细分中至少有一个点。这意味着对于2个重叠的片段,您只需要一个点......

  4. 当然还有更多选择如何解决这个问题,显而易见的是 genere&amp;使用一些启发式方法测试仅适用于重叠超过一次的片段。所以我会以这种方式攻击这个任务(使用#2 中假设的术语):

    1. r
    2. 对细分进行排序
    3. 为细分受众群数据添加重叠次数

      因此,细分受众群将为{ r,l,n },并为所有细分设置n=0

    4. 扫描分段重叠

      类似

      for (i=0;i<segments;i++) // loop all segments
       for (j=i+1;j<segments;j++) // loop all latter segments until they are still overlapped
        if ( segment[i] and segment [j] are overlapped )
         {
         segment[i].n++; // update overlap counters
         segment[j].n++;
         }
        else break;
      

      现在,如果r分类的片段重叠,那么

      segment[i].r             <=segment[j].r
      segment[i].r+segment[i].l>=segment[j].r
      
    5. 扫描处理非重叠段的段

      对于每个段,使segment[i].n==0添加到解决方案点列表,其点(中间)由距离行开头的距离定义。

      points.add(segment[i].r+0.5*segment[i].l);
      

      然后从列表中删除段(或将其标记为已使用或者您为速度提升做了什么......)。

    6. 扫描仅重叠一次的片段

      因此,如果segment[i].n==1,您需要确定它是否与i-1i+1重叠。因此,将重叠的中点添加到解决方案点,并从列表中删除i段。然后递减重叠段ni+1i-1)`,如果为零则删除它。

      points.add(0.5*( segment[j].r + min(segment[i].r+segment[i].l , segment[j].r+segment[j].l )));
      

      循环整个扫描,直到解决方案中没有新的点。

    7. 现在您只剩下多个重叠

      从这一点来说,由于两个原因,我会有点模糊:

      1. 我没有经过测试,我没有任何测试数据可以验证,更不用说我很懒。
      2. 这就像任务一样,所以有一些工作/乐趣留给你。
      3. 从一开始我就会扫描所有段并删除所有从解决方案中获得任何意义的段。您应该在解决方案中的任何更改之后执行此步骤。

        现在,您可以尝试为每个重叠的线段组生成点组合,并记住覆盖组中所有线段的最小点数。 (只是蛮力)。

        有更多的启发式方法可以处理所有两次重叠的段(与单个重叠的方式类似)但最后你将不得不对其余的数据做蛮力......

        [edit1],因为您添加了新信息

        r,l表示从行首开始的左右距离。因此,如果您想要在其他公式{ r',l' }(l<=r)之间进行转换,那么

        l=r`
        r=r`+l`
        

        并返回

        r`=l
        l`=r-l`
        

        抱歉懒得重写整件事......

答案 2 :(得分:0)

这是C语言中有效的解决方案,在阅读全文之前,请先部分引用它并尝试修复您的代码。编码愉快:) 剧透警报

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

int cmp_func(const void *ptr_a, const void *ptr_b)
{
    const long *a = *(double **)ptr_a;
    const long *b = *(double **)ptr_b;

    if (a[1] == b[1])
        return a[0] - b[0];

    return a[1] - b[1];
}

int main()
{
    int i, j, n, num_val;
    long **arr;
    scanf("%d", &n);
    long values[n];
    arr = malloc(n * sizeof(long *));

    for (i = 0; i < n; ++i) {
        *(arr + i) = malloc(2 * sizeof(long));
        scanf("%ld %ld", &arr[i][0], &arr[i][1]);
    }

    qsort(arr, n, sizeof(long *), cmp_func);

    i = j = 0;
    num_val = 0;

    while (i < n) {
        int skip = 0;
        values[num_val] = arr[i][1];

        for (j = i + 1; j < n; ++j) {
            int condition;
            condition = arr[i][1] <= arr[j][1] ? arr[j][0] <= arr[i][1] : 0;

            if (condition) {
                skip++;
            } else {
                break;
            }
        }

        num_val++;
        i += skip + 1;
    }

    printf("%d\n", num_val);

    for (int k = 0; k < num_val; ++k) {
        printf("%ld ", values[k]);
    }

    free(arr);
    return 0;
}