回 帖 发 新 帖 刷新版面

主题:第69次编程比赛题目

题目:平面上有一堆随机分布的点,都有x,y坐标。任意给定一个点A,在这些点中快速找出三个点,它们构成的三角形正好包围点A(点A可以在三角形的边上,但三角形的三个点不能在同一直线上)。要求找出的三个点离点A的距离和最小。

// x[] -- 所有点的x坐标
// y[] -- 所有点的y坐标
// n   -- 点数(3<=n<=1000000)
// x0,y0 -- 点A的坐标
// 【返回值】三个点到点A的最小距离和,如果无解则返回0
double MinDistance(int x[], int y[], int n, int x0, int y0, char *id)
{
    strcpy(id, "");    // 请在""中填入你的id,主要是方便输出测试结果
        // ...
}

判断标准:
(1)正确
(2)时间效率
(3)空间效率
(4)可读性

比赛时间:
    2008年7月17日12:00 -- 2008年7月24日12:00

回复列表 (共21个回复)

11 楼


说明:花费了5天的时间, 这题就难在判断点是否在三角形内, 这花了我大约3天多。 我分了好几个部分来粘贴此题。 题目通过DEV编译。
另外说一句, 我的数组开到10000还行, 开到100000就会出现错误。 估计是机子的问题。 我家是32位的。 不知道和这有不有关系。
题目中的边和角我认为是不同的概念(根据三角形的要素定义), 所以当点在三角形的顶点时我给予排除。

使用语言 C++
使用编译器 DEV C++ 4.9.9.2

文件包含:
●头文件        iostream   cassert   cmath   namespace(std)
●常量          EPSINON
●声明部分      
●自定义类      MyComparator
●线性规划函数  linear_programming()
●比赛函数      MinDistance()


//part 1
#include <iostream>
#include <cassert>
#include <cmath>

using namespace std;

const double EPSINON = 0.0000001; 

class MyComparator;
bool linear_programming(const MyComparator&, const MyComparator&, const MyComparator&, 
                              const int&, const int&);
double MinDistance(int[], int[], int, int, int, char);



12 楼


//part 2
class MyComparator {
public: 
        MyComparator() {
        }
        MyComparator(const int& a, const int& b, const int& x0, const int& y0):
                           x(a), y(b) {
                                 d = sqrt(pow((double)(x - x0), 2) 
                                     + pow((double)(y - y0), 2));
        }
        
        int getx() const {
            return x;
        }
        int& getx() {
             return x;
        }
        int gety() const {
            return y;
        }
        int& gety() {
             return y;
        }            
        double getd() const {
               return d;
        }
        double& getd() {
                return d;
        }        
        
private:
        int    x;
        int    y;
        double d;
};

bool linear_programming(const MyComparator& a, const MyComparator& b, const MyComparator& c, 
     const int& x0, const int& y0) {
           //非矩形区域的点排除
           if(x0 > max(a.getx(), max(b.getx(), c.getx())) ||
                  x0 < min(a.getx(), min(b.getx(), c.getx())))
                       return false;
           if(y0 > max(a.gety(), max(b.gety(), c.gety())) ||
                  y0 < min(a.gety(), min(b.gety(), c.gety())))
                       return false;
           
           //2条直线斜率相同排除
           double Kab = 0.0000000, Kbc = 0.0000000, Kac = 0.0000000;
           if(a.getx() != b.getx()) Kab = (double)(a.gety() - b.gety())/(double)(a.getx() - b.getx());
           if(b.getx() != c.getx()) Kbc = (double)(b.gety() - c.gety())/(double)(b.getx() - c.getx());
           if(a.getx() != c.getx()) Kac = (double)(a.gety() - c.gety())/(double)(a.getx() - c.getx());
           if(Kab == Kbc || Kab == Kac || Kbc == Kac)
                  return false;
           
           //点在顶点上排除 
           double Dab = sqrt(pow((double)(a.getx() - b.getx()), 2) + pow((double)(a.gety() - b.gety()), 2));
           double Dbc = sqrt(pow((double)(b.getx() - c.getx()), 2) + pow((double)(b.gety() - c.gety()), 2));
           double Dac = sqrt(pow((double)(a.getx() - c.getx()), 2) + pow((double)(a.gety() - c.gety()), 2));
           double D_sum = a.getd() + b.getd() + c.getd();
           
           if(D_sum - Dab - Dbc >= -EPSINON && D_sum - Dab - Dbc <= EPSINON) 
                    return false;
           if(D_sum - Dab - Dac >= -EPSINON && D_sum - Dab - Dac <= EPSINON) 
                    return false;
           if(D_sum - Dbc - Dac >= -EPSINON && D_sum - Dbc - Dac <= EPSINON) 
                    return false;
           
           //超过上限的排除 
           double D_border = Dab + Dbc + Dac - min(Dab, min(Dbc, Dac));
           if(D_sum > D_border) return false;
           
           return true;
}

13 楼


//part 3
double MinDistance(int x[], int y[], int n, int x0, int y0, char *id)
{
       
     strcpy(id, "abc123!!!");    // 请在""中填入你的id,主要是方便输出测试结果
     
     assert(n >=3);
     
     MyComparator My[n];
     for(int i = 0; i < n; i++) {
             MyComparator a(MyComparator(x[i], y[i], x0, y0));
             My[i].getx() = a.getx();
             My[i].gety() = a.gety();
             My[i].getd() = a.getd();
     }
     
     int i = 0;
     int j = 0;
     for(i = 0; i < n - 1; i++)
           for(j = i + 1; j < n - 1; j++)
                 if(My[j].getd() > My[j + 1].getd()) {
                                    swap(My[j].getx(), My[j + 1].getx());
                                    swap(My[j].gety(), My[j + 1].gety());
                                    swap(My[j].getd(), My[j + 1].getd());
                 }
                 
     int k = 0; i = 0; j = 0;
     double old_d = 0.0;
     
     for(i = 0; i < n - 2; i++)
           for(j = i + 1; j < n - 1; j++)
                 for(k = j + 1; k < n; k++)
                       if(linear_programming(My[i], My[j], My[k], x0, y0)) {
                            old_d = My[i].getd() + My[j].getd() + My[k].getd();
                            goto loop;
                 }
loop:
     if(n == 3) goto loop2;
     for(i = 0; i < n - 2; i++)
           for(j = i + 1; j < n - 1; j++)
                 for(k = j + 1; k < n; k++) {
                       if(k == j + 1 
                            && My[i].getd()+My[j].getd()+My[k].getd() < old_d
                               && linear_programming(My[i], My[j], My[k], x0, y0))
                                  return My[i].getd()+My[j].getd()+My[k].getd();
                                  
                       if(My[i].getd()+My[j].getd()+My[k].getd() < old_d
                               && linear_programming(My[i], My[j], My[k], x0, y0)) {
                               old_d = My[i].getd()+My[j].getd()+My[k].getd();
                               break;
                       }
                       if(k == j + 1 
                            && My[i].getd()+My[j].getd()+My[k].getd() >= old_d)
                               return old_d;
                       if(My[i].getd()+My[j].getd()+My[k].getd() >= old_d) break;
                 }
loop2:
     return old_d;
     
}    //END FUN

14 楼

最后说下
程序处理了很多例外情况,如3点共线,点在顶点,点在边上啊==

应该是不会有问题的

15 楼

这是要提交源代码是么?

16 楼

[code=c]
/***********************************************************
*            Written by Jowenshaw, 24 Jul 2008.
*            Compiled by Turbo C++ 3.0
************************************************************/

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

/////////////////////////////////////////////////////////////////////////////////////
//主要思路:先计算各点到A点的距离,然后按升序排序。
//关键在于排序后离A最近的任意一点都必定位于某个满足要求所取的三点之中。
//其中有一种特例,即所给点有点与A重合的情形,
//这种情形只需在排序后的点列中从近到远取出与A和离A最近的非重合点这两点不共线的第一个点,
//一般情形,通过N截枝法,原因就是按排序后的下标从小到大取点,若某个可能解中的最大下标为N,那么
//可以肯定优于这组解中的点的下标必小于N。本程序便是通过这样逐次缩小选取范围的。
//难点在于判断所选三点组成三角形且包含点A。
/////////////////////////////////////////////////////////////////////////////////////

double MinDistance(int x[], int y[], int n, int x0, int y0, char* id)
{
    const double INFITY = 10000000.0;       //定义一个充分大的距离上界
    int i, j;              //循环变量
    int ntemp, N;          //临时变量,ntemp用于交换,N指可取Q[]中的点列的最大下标
    double ftemp;          //临时变量,ftemp用于交换
    double *dist;          //dist[i]存取P[i]点到A的距离
    int *ind;              //ind[i]为p[i]在按到点A的距离排序后所处的下标
    double curMinVal, minVal = INFITY; //对应所选取的三点到点A的距离之和,用于记录当前值和全局最小值
    int flag = 0;          //flag作为取点是否成功的标志,0表示不成功,1表示成功,也即有无解的标志

    strcpy(id, "jowenshaw");
    if (n < 3) return 0;   //无解的特殊情形

    dist = (double *) malloc( n * sizeof (double) );
    ind  = (int *) malloc( n * sizeof (int) );

    //计算P[i]点到A的距离,存与数组dist[], 同时初始化数组ind[]
    for (i = 0; i < n; i ++)
    {
        dist[i] = (x[i] - x0) * (x[i] - x0) + (y[i] - y0) * (y[i] - y0);
        ind [i] = i;
    }

    //用插入排序法对数组dist[]排序
    for (i = 1; i < n; i ++)
    {
        for (j = i; j < n; j ++)
        {
            if (dist[j] < dist[j-1])
            {
                ftemp = dist[j-1];
                dist[j-1] = dist[j];
                dist[j] = ftemp;
                ntemp = ind[j-1];
                ind[j-1] = ind[j];
                ind[j] = ntemp;
            }
        }//inner for
    }//outer for

    //记排序后的点列为Q[i], 即令Q[i] = P[ ind[i] ], 其中 i = 0, 1, ..., n-1.
    //再分情形讨论:

    //(1) Q[0] 和 A 重叠
    if (x[ ind[0] ] == x0 && y[ ind[0] ] == y0)
    {
        for (i = 2; i < n; i ++)
        {//判断Q[0], Q[1], Q[i]是否共线,然后处理
            if ( (x[ ind[1] ] - x0) * (y[ ind[i] ] - y0)          \
                 != (y[ ind[1] ] - y0) * (x[ ind[i] ] - x0) )
            {
                flag = 1;
                break;
            }
        }        
        minVal = sqrt( dist[1] )+ sqrt( dist[i] );
        free(dist);
        free(ind);

        if (1 == flag)  return minVal;
        return 0;
       
    }
[/code]

17 楼


[code=c]
/***********************************************************
*            Written by Jowenshaw, 24 Jul 2008.
*            Compiled by Turbo C++ 3.0
************************************************************/
  //(2) Q[0] 和 A 不重叠
    for (N = n, i = 1; i < n; i ++)
    {
        j = i + 1;
        while (j < N)
        {//判断Q[0],Q[i],Q[j]是否组成包含A点的三角形,然后处理
            if (     (  (y[ ind[i] ] - y0) * (x[ ind[0] ] - x0) -       \
                        (y[ ind[0] ] - y0) * (x[ ind[i] ] - x0)         \
                     ) *                                                \
                     (  (y[ ind[j] ] - y0) * (x[ ind[0] ] - x0) -       \
                        (y[ ind[0] ] - y0) * (x[ ind[j] ] - x0)         \
                     )   <= 0                                           \
                  &&                                                    \
                     (  (y[ ind[0] ] - y0) * (x[ ind[i] ] - x0) -       \
                        (y[ ind[i] ] - y0) * (x[ ind[0] ] - x0)         \
                     )*                                                 \
                     (  (y[ ind[j] ] - y0) * (x[ ind[i] ] - x0) -       \
                        (y[ ind[i] ] - y0) * (x[ ind[j] ] - x0)         \
                     )   <= 0                                           \
                  &&                                                    \
                     (x[ ind[i] ] - x[ind[0]])*(y[ind[j]] - y[ind[0]])  \
                 != (y[ ind[i] ] - y[ind[0]]) * (x[ind[j]] - x[ind[0]]) \
               )            
            {//Q[0],Q[i],Q[j]组成包含A点的三角形
                flag = 1;
                N = j;
                curMinVal = sqrt( dist[0] )+ sqrt(dist[i]) + sqrt(dist[j]);
                if (curMinVal < minVal)
                {
                    minVal = curMinVal;
                }
            }//if
            else
            {//Q[0],Q[i],Q[j]不组成包含A点的三角形
                j++;
            }
        }//while
    }//for           
    free(dist);
    free(ind);
    if (1 == flag)   return minVal;
    return 0;
}

//小测试程序,采用随机生成数
#include <time.h>
int main()
{
    int i;
    int n = 5;     //可以修改n来调试程序
    int x0, y0;
    double res;
    int *x = (int *) malloc( n * sizeof (int) );
    int *y = (int *) malloc( n * sizeof (int) );

    char *id = (char *) malloc(100);

    x0 = y0 = 0;
    srand( time(0) );
    for (i = 0; i < n; i ++)
    {
    x[i] = int ( n * rand() / 1000 ) - n / 2;
        y[i] = int ( n * rand() / 1000 ) - n / 2;
        //printf("(%8d, %8d)\n",x[i], y[i]);
    }
    res = MinDistance(x, y, n, x0, y0, id);
    printf("\n%s:\t%f\n", id, res);

    free(x);
    free(y);
    free(id);

    return 0;
}[/code]

18 楼

#include <iostream>
#include <limits>
#include <cmath>
#include <cstdlib>
#include <cassert>
#include <ctime>

typedef struct
{
    double x;
    double y;
}Point;

#define DOUBLE_EPSILON 1.0e-6
//#define DOUBLE_EPSILON    numeric_limits<double>::epsilon()

//直角坐标内两点之间的距离
inline double DistancePointToPoint(const Point &point1, const Point &point2)
{
    double xtmp = point2.x - point1.x;
    double ytmp = point2.y - point1.y;
    return sqrt(xtmp * xtmp + ytmp * ytmp);
}

//通过三角形的三条边长计算面积
inline double TriangleArea(const double &dfDistance_AB, const double &dfDistance_AC, const double &dfDistance_BC)
{
    double dfHalfTotal = (dfDistance_AB + dfDistance_AC + dfDistance_BC) / 2.0;
    return sqrt((dfHalfTotal - dfDistance_AB) * (dfHalfTotal - dfDistance_AC) * (dfHalfTotal - dfDistance_BC) * dfHalfTotal);
}

//判断点ABC能否构成的三角形,且正好包围点D
//(点D可以在三角形的边上,但三角形的三个点不能在同一直线上)
inline bool isAndInTheTraingle(const Point &pointA, const Point &pointB, const Point &pointC, const Point &pointD)
{
    double dfDistance_AB = DistancePointToPoint(pointA, pointB);
    double dfDistance_AC = DistancePointToPoint(pointA, pointC);
    double dfDistance_BC = DistancePointToPoint(pointB, pointC);    
    if (abs(dfDistance_AB + dfDistance_AC - dfDistance_BC) <= DOUBLE_EPSILON
        || abs(dfDistance_AB + dfDistance_BC - dfDistance_AC) <= DOUBLE_EPSILON
        || abs(dfDistance_AC + dfDistance_BC - dfDistance_AB) <= DOUBLE_EPSILON)
    {
        return false;
    }
    double dfDistance_AD = DistancePointToPoint(pointA, pointD);
    double dfDistance_BD = DistancePointToPoint(pointB, pointD);
    double dfDistance_CD = DistancePointToPoint(pointC, pointD);
    if (TriangleArea(dfDistance_AB, dfDistance_AD, dfDistance_BD) 
        + TriangleArea(dfDistance_AC, dfDistance_AD, dfDistance_CD)
        + TriangleArea(dfDistance_BC, dfDistance_BD, dfDistance_CD) 
        > TriangleArea(dfDistance_AB, dfDistance_AC, dfDistance_BC))
    {
        return false;
    }
    return true;    
}

typedef struct
{
    //本题目,点(x, y)到题目中点A 的距离
    Point point;
    double dfDistanceToPointA;
}Distance;

int DistanceCmp(const void *p1, const void *p2)
{
    const Distance *pDistance1 = (const Distance*)p1;
    const Distance *pDistance2 = (const Distance*)p2;
    if (pDistance1->dfDistanceToPointA > pDistance2->dfDistanceToPointA)
    {
        return 1;
    }
    else if (pDistance1->dfDistanceToPointA < pDistance2->dfDistanceToPointA)
    {
        return -1;
    }
    return 0;
}

19 楼



double MinDistance(int x[], int y[], int n, int x0, int y0, char *id)
{
    strcpy(id, "ckeryradish");    // 请在""中填入你的id,主要是方便输出测试结果
    Distance *pDistanceToPointA = new(std::nothrow) Distance[n];
    if (NULL == pDistanceToPointA)
    {
        std::cout << "out of memory!" << std::endl;
        return 0.0;
    }
    int i = 0;
    Point pointA;
    pointA.x = x0;
    pointA.y = y0;
    for (i = 0; i < n; i++)
    {
        pDistanceToPointA[i].point.x = x[i];//认为x[i] y[i] 分别是点i的x坐标和y坐标
        pDistanceToPointA[i].point.y = y[i];
        pDistanceToPointA[i].dfDistanceToPointA    = DistancePointToPoint(pDistanceToPointA[i].point, pointA);
    }
    qsort(pDistanceToPointA, n, sizeof(pDistanceToPointA[0]),DistanceCmp);
    bool bContainAnswer = false; //是否存在解
    Point point1;
    Point point2;
    Point point3;        
    int j = 0;
    int k = 0;
    for (i = 0; i < n - 2; i ++)
    {
        point1 = pDistanceToPointA[i].point;
        for (j = i + 1; j < n - 1; j++)
        {
            point2 = pDistanceToPointA[j].point;        
            for (k = j + 1; k < n; k++)
            {
                point3 = pDistanceToPointA[k].point;            
                if(isAndInTheTraingle(point1, point2, point3, pointA))
                {
                    bContainAnswer = true;    
                    goto Label;
                }
            }
        }
    }
    
Label:
    if (!bContainAnswer)
    {
        std::cout << "No answer !" << std::endl;
        delete [] pDistanceToPointA;
        return 0.0;         
    }
    double dfMinDistance = pDistanceToPointA[i].dfDistanceToPointA
        + pDistanceToPointA[j].dfDistanceToPointA
        + pDistanceToPointA[k].dfDistanceToPointA;
    
    //寻在第一个点在i+1至k-1之间时可能的最优解
    //当第一个点到达k时,已无最优解
    int m = k;
    for (i++; i < m ; i ++)
    {
        if (3.0 * pDistanceToPointA[i].dfDistanceToPointA >= dfMinDistance)        
        {
            break;
        }
        point1 = pDistanceToPointA[i].point;
        for (j = i + 1; j < n - 1; j++)
        {
            if (pDistanceToPointA[i].dfDistanceToPointA + 2.0 * pDistanceToPointA[j].dfDistanceToPointA >=  dfMinDistance)
            {
                break;
            }
            point2 = pDistanceToPointA[j].point;
            for (k = j + 1; k < n; k++)
            {
                double dfDistance = pDistanceToPointA[i].dfDistanceToPointA
                    + pDistanceToPointA[j].dfDistanceToPointA
                    + pDistanceToPointA[k].dfDistanceToPointA;                
                if (dfDistance >= dfMinDistance)                            
                {
                    break;
                }
                point3 = pDistanceToPointA[k].point;
                if(isAndInTheTraingle(point1, point2, point3, pointA))
                {
                    dfMinDistance = dfDistance;
                }
            }
        }
    }
    
    delete [] pDistanceToPointA;
    return dfMinDistance;    
}

20 楼

have a look

我来回复

您尚未登录,请登录后再回复。点此登录或注册