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

本文探讨了一种在正方形区域内,寻找特定点以使连接这些点与正方形顶点的线段总长度达到最小的问题,并给出了针对不同点数的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)题面:

Description

Given a square at [0, 1] * [0, 1] that has N points ( P1, P2, ..., PN ) 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 (P1, P2, ..., PN) to record the graph length using this way of connecting. 

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

Given the initial positions of N points, your task is to find out N points P1", P2", ..., PN" in the square such that |P1P1"| + |P2P2"| + ... + |PNPN"| is minimum and LEN (P1", P2", ..., PN") = LEN (P1', P2', ..., PN') . You are requested to output the value of |P1P1"| + |P2P2"| + ... + |PNPN"|, where |PiPi"| is the distance between Pi and Pi". 

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

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 |P1P1"| + |P2P2"| + ... + |PNPN"|. 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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值