sgu120


SGU120 Archipelago
题目大意:

Ber群岛由N个岛屿组成。将岛屿视作点,则组成它的岛屿围成了一个正N边形。

将岛屿按顺时针标号,岛屿N1的坐标是(x1,y1),岛屿N2的坐标是(x2,y2)

你的任务是 得到 N1,N2的坐标 求出所有N个点的坐标

输入:

在第一行,输入 N ,N1 和 N2 (3 ≤ N ≤ 150, 1 ≤ N1,N2 ≤ N, N1 ≠ N2) 由空格隔开。

接下来两行输入 N1 和 N2 的坐标(坐标的绝对值不会大于 2000000)

输出:

输出N行,表示N个点的坐标,按照点的标号顺序输出。结果保留6位小数。

样例输入:

4 1 3

1.0000 0.0000

1.0000 2.0000

样例输出:

1.000000 0.000000

0.000000 1.000000

1.000000 2.000000

2.000000 1.000000


又是计算几何...
题目就是由两个点还原一个正多边形
为了方便,假定a<b

下面介绍向量旋转方法:
思想很简单:找到正多边形的中心(内心)(外心和内心是同一个...)
引用一些性质:
性质一:正多边形任意两点的连线的中垂线经过该正多边形的中心。
证明:根据中垂线性质:中垂线上的点到两点的距离相等。
      那么对于任意一个点i,i与其他点连线的中垂线交点到所有点的距离都相同。
      与正多边形所有点距离相同的点就是中心。

性质二:对于向量(x,y)在逆时针旋转a度之后得到向量为(x*cosa-y*sina,x*sina+y*cosa)
证明的话可以自己在草稿纸上画一画,很容易证明。

有了以上性质,我们可以给出大致思路
1.根据两个点的连线找出中垂线,并且大致判断出中心的方向(即从两点的中点指向中心的向量方向)。
  设中点为(xmid,ymid)
  从中点向b点指一个向量。
  若从a到b所经过的点>从b到a经过的点,则证明中心在a到b的方向上,将上面的向量逆时针旋转90°,否则将上面的向量顺时针旋转90°

2.根据两个点的序号关系得到两点与中心构成的中心角角度。
  对于两个点a、b,中心角度为:a、b两点连线与中心的另一端的边数*(360°/n)。

3.根据中心角角度和1中的向量方向计算出中心的坐标。
  设中心坐标为(xcenter,ycenter)
  xcenter=xmid+lx*t;
  ycenter=ymid+ly*t;
  tan(cita/2)=DIS(mid,a)/DIS(center,mid)
  将式子展开并合并,可以得到t与xcenter、ycenter。

4.从中心坐标引一条向量指向一个点。
5.通过旋转角度得到其他点的坐标。

听说有大神用相似做的,精度很高,但是很难想。

注意事项:1.计算机一般使用弧度制。
          2.WA在TEST5的OIer,请仔细检查向量的旋转(也有可能是cita/2的问题)。
下面附上我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define PI acos(-1)
struct p
{
  double x,y;
}point[200],mid,center,l;
double numangle,length,cita,t,sum;
int n,a,b;
double SQR(double a)
{
  return a*a;
}
double SQRDIS(struct p a,struct p b)
{
  return SQR(a.x-b.x)+SQR(a.y-b.y);
}
struct p turn(struct p a,double angle)//(x,y)->(x*cosa-y*sina,x*sina+y*cosa)
{
  struct p re;
  re.x=a.x*cos(angle)-a.y*sin(angle);
  re.y=a.x*sin(angle)+a.y*cos(angle);
  return re;
}
struct p add(struct p a,struct p b)
{
  struct p re;
  re.x=b.x-a.x;
  re.y=b.y-a.y;
  return re;
}
void makepoint()
{
  int i;
  l=add(center,point[a]);
  i=a;
  do
  {
  point[i].x=center.x+l.x;
  point[i].y=center.y+l.y;
  l=turn(l,-numangle);
  i++;
  if (i>n)
    i=1;
  }while (i!=a);
  return ;
}
void work()
{
  if (b-a-1>a+n-b-1)
    {
	l=turn(l,PI/2);
	cita=(a+n-b)*(numangle);
    }
  else
    {
    l=turn(l,-PI/2);
    cita=(b-a)*(numangle);
    }
  cita/=2;
  length=SQRDIS(mid,point[a]);
  length/=SQR(tan(cita));
  sum=SQR(l.x)+SQR(l.y);
  t=sqrt(length/sum);
  center.x=mid.x+l.x*t;
  center.y=mid.y+l.y*t;
  makepoint();
  return ;
}
void init()
{
  int i;
  scanf("%d",&n);
  scanf("%d%d",&a,&b);
  scanf("%lf%lf",&point[a].x,&point[a].y);
  scanf("%lf%lf",&point[b].x,&point[b].y);
  if (a>b)
    { i=a; a=b; b=i; }
  numangle=(2*PI/n);
  mid.x=(point[a].x+point[b].x)/2;
  mid.y=(point[a].y+point[b].y)/2;
  l=add(mid,point[b]);
  work();
  for (i=1;i<=n;i++)
    printf("%lf %lf\n",point[i].x,point[i].y);
  return ;
}
int main()
{
  init();
  return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值