主题:第69次编程比赛结果
boxertony [专家分:23030] 发布于 2008-07-27 18:08:00
题目贴:http://www.programfan.com/club/post-280816.html
-------------------------------------------------------------
2楼(liyaha):最朴素的算法,效率很低,但是结果正确。n超过2000就慢得要命,呵呵。
4楼(scutdx2005):算法的基本思路是对的,就是按距离排序,然后从最小距离选择,逐步扩大选择的范围。但是结果错误。
5-7楼(apart789):算法没太看懂,呵呵。结果错误。
9楼(ayxg):算法基本同4楼。结果对很多数据都错误。
11-13楼(abc123!!!):算法也是没看太明白,似乎基本思路同2楼。结果错误。
16-17楼(jowenshaw):算法也是先对距离排序再选择。结果错误。
18-19楼(ckeryradish):算法与4楼类似,但是判断三角形是否有效是通过面积来进行的。结果对很多数据都错误。
无楼(tjs125):算法也是先排序,然后选择。tjs125在循环条件的选择上,和在最后退出条件上做了很大改进,所以效率非常高。可惜的是结果对少部分数据错误,比如:p0,p1,p2,p3分别是(30,38),(46,64),(77,23),(17,38)时是有解的,但tjs125程序判断为无解;而且当n>=5000后,耗时就比较长了。估计可能是判断三角形的有效性上存在问题。(晕,刚才才发现你今天早上又修改了程序,只好重测)
综上所述,只有2楼的程序是正确的。因此我宣布本次编程比赛的冠军是liyaha。大家祝贺liyaha并请他主持下次的编程比赛。
(关于测试数据:由于我的题目上说明是随即分布的点,因此所有的点的坐标都是通过随机函数生成的,点A的坐标也是通过随机数生成的)
最后更新于:2008-07-27 19:01:00
回复列表 (共28个回复)
沙发
boxertony [专家分:23030] 发布于 2008-07-27 19:07:00
// 下面是我写的程序
// 当n=1000,000时,耗时1.3秒左右(主要时间花在排序上了)
typedef struct
{
int x, y;
double dist;
}Point;
inline double GetDist(int x1, int y1, int x2, int y2)
{
return sqrt(((double)x2-x1)*(x2-x1)+((double)y2-y1)*(y2-y1));
}
// 求(p1-p0)和(p2-p0)的叉积
inline double CrossProduct(int x0, int y0, int x1, int y1, int x2, int y2)
{
return ((x1-x0)*(y2-y0)-(x2-x0)*(y1-y0));
}
// 判断点(x0,y0)是否在其他三点组成的三角形内,并判断三角形的有效性
bool PtInTriangle(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
{
double cp1, cp2;
cp1 = CrossProduct(x0, y0, x1, y1, x2, y2);
cp2 = CrossProduct(x0, y0, x1, y1, x3, y3);
if(cp1 * cp2 < 0) // 点p2或p3在p0p1直线的两边,允许
{
cp1 = CrossProduct(x2, y2, x3, y3, x0, y0);
cp2 = CrossProduct(x2, y2, x3, y3, x1, y1);
if(cp1 * cp2 > 0) // 点p0和p1位于直线p2p3的同一边,允许
return 1; // 此时实际上p0还不一定在三角形p1p2p3内;但如果不在三角形内,必定有p0的x或者y坐标大于所有的三个点p1p2p3的x或者y坐标
else if(cp1 == 0 && cp2 != 0) // 点p0在p2p3上
return 1;
else
return 0;
}
else if(cp1 == 0 && cp2 != 0)
{
if((x1-x0)*(x0-x2) > 0 || (y1-y0)*(y0-y2) > 0 // 点p0在p1p2线段上
|| x0 == x2 && y0 == y2) // 点p0与p2重合
return 1; //
}
else if(cp1 != 0 && cp2 == 0)
{
if((x1-x0)*(x0-x3) > 0 || (y1-y0)*(y0-y3) > 0 // 点p0在p1p3线段上
|| x0 == x3 && y0 == y3) // 点p0与p3重合
return 1; //
}
else if(cp1 == 0 && cp2 == 0)
{
if(x0 == x1 && y0 == y1 // p0与p1重合
&& (x1 != x2 || y1 != y2) // p1 != p2
&& (x1 != x3 || y1 != y3) // p1 != p3
&& (x2 != x3 || y2 != y3)) // p2 != p3
return 1;
}
return 0;
}
// 用于快速排序
int cmp(const void *x1, const void *x2)
{
Point a = *(Point *)x1;
Point b = *(Point *)x2;
if(a.dist >= b.dist)
return 1;
else
return -1;
}
板凳
boxertony [专家分:23030] 发布于 2008-07-27 19:08:00
double MinDistance(int x[], int y[], int n, int x0, int y0, char *id)
{
Point *points = new Point[n];
assert(points);
int i, j, k;
double distMin = DBL_MAX, dist, distExit;
int minx = INT_MAX, maxx = -INT_MAX, miny = INT_MAX, maxy = -INT_MAX;
for(i=0; i<n; ++i)
{
points[i].x = x[i];
points[i].y = y[i];
points[i].dist = GetDist(x0, y0, x[i], y[i]);
if(minx > x[i])
minx = x[i];
if(maxx < x[i])
maxx = x[i];
if(miny > y[i])
miny = y[i];
if(maxy < y[i])
maxy = y[i];
}
if(x0 < minx || x0 > maxx || y0 < miny || y0 > maxy)
goto end;
qsort(points, n, sizeof(Point), cmp);
// 从距离最小的三个点开始进行考察
// 这儿看起来有三重循环,但实际上由于是随机分布的点,极端情况出现的概率是非常低的。
// 因此大部分情况下,该三重循环的循环次数都很少。我实际测试了一下,当n=1000,000时,
// 最内循环的次数绝大部分情况都少于1000次。
for (i=2; i<n; i++)
{
distExit = points[0].dist + points[1].dist + points[i].dist;
for(j=0; j<i-1; j++)
{
for(k=j+1; k<i; k++)
{
// 排除明显不合适的三角形
if(x0 > points[i].x && x0 > points[j].x && x0 > points[k].x)
continue;
if(x0 < points[i].x && x0 < points[j].x && x0 < points[k].x)
continue;
if(y0 > points[i].y && y0 > points[j].y && y0 > points[k].y)
continue;
if(y0 > points[i].y && y0 > points[j].y && y0 > points[k].y)
continue;
if(PtInTriangle(x0, y0, points[i].x, points[i].y, points[j].x, points[j].y, points[k].x, points[k].y))
{
dist = points[i].dist + points[j].dist + points[k].dist;
if(distMin > dist)
distMin = dist;
if(distMin <= distExit)
goto end;
}
}
}
}
end:
delete []points;
return distMin!=DBL_MAX?distMin:0;
}
3 楼
s110 [专家分:1060] 发布于 2008-07-27 23:24:00
[quote]// 下面是我写的程序
// 当n=1000,000时,耗时1.3秒左右(主要时间花在排序上了)
[/quote]
这个是怎么测出来的???我如果想自己测代码效率什么的要怎么测???
4 楼
boxertony [专家分:23030] 发布于 2008-07-28 09:05:00
用clock()函数或者GetTickCount()
函数调用前后的时间相减即可
5 楼
s110 [专家分:1060] 发布于 2008-07-28 11:35:00
这样得出的结果不是很准确啊,因为还需要考虑环境的影响问题。
如果测第一个的时候,KAV没扫描;二测第二个的时候,KAV开始扫描,那两者的差距就大啦~
另外,如果代码量大,函数很多,那么每个函数调用弄一个 clock来减,这样的方法似乎不妥。
当然这样能得到一个大概的结果,我只是想找到一个更精确,更有效率的办法。
如果 LZ有兴趣一起讨论的话,请到我发的帖子,大家一起研究一下,如果LZ有更好的方法,就请赐教。
http://bbs.pfan.cn/post-281523.html (测试程序效率的方法)
6 楼
boxertony [专家分:23030] 发布于 2008-07-28 14:43:00
是的,不精确,受到很多因素的干扰,呵呵。比如软硬件环境,测试时机,编译器,编译模式等等。
但是,一个是没有必要太精确,对于大量的代码也没必要每个函数都要去测试他的时间消耗,你需要测试的也只是关键的部分而已。而且很多时候通过分析代码就可以很容易确定他的时间复杂度,这个可能比实际测试更准确。如果你实在需要那么精确,可以去分析每个函数所用的指令数(这个一是很难,另外也不一定太准确,因为同样的代码不同编译器得到的指令数也不一样),Donald.E.Knuth在他的《计算机程序设计艺术》一书中,分析算法时,就精确到了指令的条数,呵呵。
7 楼
s110 [专家分:1060] 发布于 2008-07-28 15:15:00
[quote]是的,不精确,受到很多因素的干扰,呵呵。比如软硬件环境,测试时机,编译器,编译模式等等。
但是,一个是没有必要太精确,对于大量的代码也没必要每个函数都要去测试他的时间消耗,你需要测试的也只是关键的部分而已。而且很多时候通过分析代码就可以很容易确定他的时间复杂度,这个可能比实际测试更准确。如果你实在需要那么精确,可以去分析每个函数所用的指令数(这个一是很难,另外也不一定太准确,因为同样的代码不同编译器得到的指令数也不一样),Donald.E.Knuth在他的《计算机程序设计艺术》一书中,分析算法时,就精确到了指令的条数,呵呵。[/quote]
此贴应评 30分
8 楼
ayxg [专家分:50] 发布于 2008-07-28 21:09:00
楼主能把产生随机数的程序 和一些测试数据发出来吗?谢谢!
9 楼
boxertony [专家分:23030] 发布于 2008-07-29 09:38:00
因为都是随机数据,发出来是没意义的,我也没特意保存哪一份数据,呵呵。至于产生的程序,其实就是不断调用rand()来生成x,y的坐标而已。当然在之前需要用srand初始化一下。回帖的网友中有两个的main函数就有代码。
10 楼
liyaha [专家分:0] 发布于 2008-07-29 11:02:00
晕了,没想到是我啊,当时写完程序测试时我就知道玩完,n值大了跑的太慢了,想着要对得起花费的时间就把它传上来了……还得向各位多学习啊,关于出题,谁能教教我呢?有什么规则 新手啊新手
我来回复