主题:第69次编程比赛题目
boxertony [专家分:23030] 发布于 2008-07-17 11:23:00
题目:平面上有一堆随机分布的点,都有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 楼
abc123!!! [专家分:1080] 发布于 2008-07-22 16:56:00
说明:花费了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 楼
abc123!!! [专家分:1080] 发布于 2008-07-22 16:56:00
//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 楼
abc123!!! [专家分:1080] 发布于 2008-07-22 16:57:00
//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 楼
abc123!!! [专家分:1080] 发布于 2008-07-22 17:00:00
最后说下
程序处理了很多例外情况,如3点共线,点在顶点,点在边上啊==
应该是不会有问题的
15 楼
pkqs90 [专家分:100] 发布于 2008-07-22 22:00:00
这是要提交源代码是么?
16 楼
jowenshaw [专家分:0] 发布于 2008-07-24 07:59:00
[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 楼
jowenshaw [专家分:0] 发布于 2008-07-24 08:00:00
[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 楼
ckeryradish [专家分:140] 发布于 2008-07-24 09:29:00
#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 楼
ckeryradish [专家分:140] 发布于 2008-07-24 09:32:00
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;
}
我来回复