POJ_1696
一个比较直观的思路就是,如果现在我在某个点,那么我就要沿着刚刚走过的一段的方向看过去,然后视线逐渐向左转,遇到第一个点为止,然后就冲那个点走过去即可。对于起点的选择也并不难想到,挑一个y值最小的点即可,初始的视线方向就定为沿x轴正方向,如果有多个y值最小的点,那就随便选一个,因为无论选哪一个最终都是可以走出合法的路线的。
剩下一个问题就是如果判断视线左转时遇到的第一个点了,出于方便处理的考虑,我们可以选用左转的角度theta来衡量,而theta可以通过点积及相关运算得到。同时,我们注意到theta是在0到pi之间的,于是我们也可以选用一个等价的变量cos(theta)来衡量,而且后者更简便一些,毕竟前者也是通过后者做acos的运算得到的。
这个题有一个地方我没有去处理,就是如果存在重合的点的话,这样做就会出现除0的情况。但是根据题意来看应该不会有重点的情况,没考虑这点的情况下交了之后也AC了,所以数据里面应该也没有重点的情况。
#include<stdio.h>
#include<string.h>
#include<math.h>
#define MAXD 60
int N, vis[MAXD], S;
double x[MAXD], y[MAXD], px, py, cx, cy;
void init()
{
int i, j;
py = 110;
scanf("%d", &N);
for(i = 1; i <= N; i ++)
{
scanf("%d%lf%lf", &j, &x[i], &y[i]);
if(y[i] < py)
py = y[i], px = x[i] - 1, S = i;
}
}
void solve()
{
int i, j, k;
double t, a, len;
cx = x[S], cy = y[S];
memset(vis, 0, sizeof(vis));
vis[S] = 1;
printf("%d %d", N, S);
for(i = 1; i < N; i ++)
{
a = -2, len = sqrt((cx - px) * (cx - px) + (cy - py) * (cy - py));
for(j = 1; j <= N; j ++)
if(!vis[j])
{
t = ((cx - px) * (x[j] - cx) + (cy - py) * (y[j] - cy)) / (sqrt((x[j] - cx) * (x[j] - cx) + (y[j] - cy) * (y[j] - cy)) * len);
if(t > a)
a = t, k = j;
}
vis[k] = 1;
px = cx, py = cy;
cx = x[k], cy = y[k];
printf(" %d", k);
}
printf("\n");
}
int main()
{
int t;
scanf("%d", &t);
while(t --)
{
init();
solve();
}
return 0;
}