POJ-2053-Square(费马点+思维)

(一)题面:

Description

Given a square at [0, 1] * [0, 1] that has N points ( P 1, P 2, ..., P N ) in the square (you may assume that different points can be at the same position), we can connect the N points and the four corners of the square with some line segments so that through these segments any two of the N+4 points can reach each other (directly or indirectly). The graph length is defined as the total length of the line segments. When N points' positions are fixed, there must exist a way of connecting them, such that it will make the shortest graph length. We can use LEN (P 1, P 2, ..., P N) to record the graph length using this way of connecting. 

In this situation, LEN (P 1, P 2, ..., P N) is a function of P 1, P 2, ..., P N. When P 1, P 2, ..., P N change their positions, LEN (P 1, P 2, ..., P N) also changes. It's easy to prove that there exist some P 1', P 2', ..., P N' in the square such that LEN (P 1', P 2', ..., P N') is at its minimum. 

Given the initial positions of N points, your task is to find out N points P 1", P 2", ..., P N" in the square such that |P 1P 1"| + |P 2P 2"| + ... + |P NP N"| is minimum and LEN (P 1", P 2", ..., P N") = LEN (P 1', P 2', ..., P N') . You are requested to output the value of |P 1P 1"| + |P 2P 2"| + ... + |P NP N"|, where |PiPi"| is the distance between Pi and Pi". 

For example, Figure-1 gives the initial position of P 1 and the way of connecting to obtain LEN (P 1). In Figure-2, it gives the position of P 1", which is at the center of the square, and the way of connecting to obtain LEN (P 1"). It can be proved that LEN (P 1") = LEN (P 1’); your job is to output the distance between P 1 and P 1".

Input

The input consists of several test cases. For each test case, the first line consists of one integer N (1 <= N <= 100), the number of points, and N lines follow to give the coordinates for every point in the following format: 
x y 

Here, x and y are float numbers within the value [0, 1]. 

A test case of N = 0 indicates the end of input, and should not be processed. 

Output

For each test case, output the value of |P 1P 1"| + |P 2P 2"| + ... + |P NP N"|. The value should be rounded to three digits after the decimal point.

Sample Input

1
0.2 0.5
2
0 0.5
0.5 0.5
0

Sample Output

0.300
0.500

(二)题目大意:

        在一个正方形中给出n个点S,让你找到另外n个点S',使得正方形的四个顶点以及S'中所有点相连的线段总的长度和最小,且使得S中的点到S'中的对应点的距离之和最小(可能直接看题目更清楚QWQ)。

(三)解题思路:

        ①考虑一个点,那么找的点就是正方形的中心,然后求一下和原来给出的点的距离即可。

        ②考虑两个点,如果两个点不重合,根据对称性思维,则找的两个点大致上如下图一位置:

                

        ③再想具体再哪一个位置。将正方形分成左右两个部分,那么我们可以得到两个三角形(图二中红色三角形),接下来不难想到找的两个点应该分别是两个三角形的费马点。同理我们将矩形分成上下两个部分可以得到图三的两个点。计算后可得总的长度为1+sqrt(3),比在中心时的距离2*sqrt(2)要小。说明两个点的时候,上图最优。

        ④对于更多的点呢,这里只有一个猜想:当n>2时,最小距离和也由上图得到。那么接下来就是先在给出的n个点中找最优的两个点放到图中的两个点的位置,其余的n-2个点就映射到五条线段上的最近的位置即可(分为图二和图三两种情况,取最小值)。

        ⑤费马的点的定义及求解具体百度~(注意这里的两个三角形是等腰直角三角形)。

(四)具体代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define esp 1e-6
using namespace std;
int n;
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){x=_x;y=_y;}
    Point operator - (const Point& b)const{
        return Point(x-b.x,y-b.y);
    }
    double operator * (const Point& b)const{
        return x*b.x+y*b.y;
    }
}p[110];
struct Line{
    Point s,e;
    Line(Point p1,Point p2){
        s=p1,e=p2;
    }
};
double best=1e9;
const Point only(0.5,0.5);
const Point one(sqrt(3.0)/6.0,0.5);
const Point two(1.0-sqrt(3.0)/6.0,0.5);
const Point three(0.5,sqrt(3.0)/6.0);
const Point four(0.5,1.0-sqrt(3.0)/6.0);
const Point lu(0,1),ld(0,0),ru(1,1),rd(1,0);
const Line Line1(one,two),Line2(lu,one),Line3(ru,two),Line4(ld,one),Line5(rd,two);
const Line Line6(three,four),Line7(rd,three),Line8(lu,four),Line9(ld,three),Line10(ru,four);
Line line1[5]={Line1,Line2,Line3,Line4,Line5};
Line line2[5]={Line6,Line7,Line8,Line9,Line10};
double dist(Point n1,Point n2){
    return sqrt((n1.x-n2.x)*(n1.x-n2.x)+(n1.y-n2.y)*(n1.y-n2.y));
}
double Get_nearest_point(Point P,Line L){
    Point result;
    double t=((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
    if(t>=0.0&&t<=1.0){
        result.x=L.s.x+(L.e.x-L.s.x)*t;
        result.y=L.s.y+(L.e.y-L.s.y)*t;
    }
    else{
        if(dist(P,L.s)-dist(P,L.e)<esp)result=L.s;
        else result=L.e;
    }
    return dist(result,P);
}
double Get_dist_to_line(int x,Line l[5]){
    double _Min=1e9;
    for(int i=0;i<5;i++)
        _Min=min(_Min,Get_nearest_point(p[x],l[i]));
    return _Min;
}
double solve(Line l[5],Point p1,Point p2){
    int pos1,pos2;
    double ans=0,_Min=1e9;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(i==j)continue;
            double d1=dist(p[i],p1),d2=dist(p[j],p2);
            double d3=Get_dist_to_line(i,l),d4=Get_dist_to_line(j,l);
            if(_Min>d1-d3+d2-d4){_Min=d1-d3+d2-d4;pos1=i;pos2=j;}
        }
    }
    ans=dist(p[pos1],p1)+dist(p[pos2],p2);
    for(int i=0;i<n;i++){
        if(i==pos1||i==pos2)continue;
        ans+=Get_dist_to_line(i,l);
    }
    return ans;
}
int main(){
    freopen("in.txt","r",stdin);
    while(~scanf("%d",&n)&&n){best=1e9;
        for(int i=0;i<n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);
        if(n==1){printf("%.3lf\n",dist(p[0],only));continue;}
        printf("%.3lf\n",min(solve(line1,one,two),solve(line2,three,four))+esp);
    }
    return 0;
}

(五)总结:

        其实这个题和费马点的关系并不是特别大了...。总之,敢猜敢想,说不定就猜对了呢23333。

猜你喜欢

转载自blog.csdn.net/xbb224007/article/details/80373102
今日推荐