Fermat Point in Quadrangle POJ 3990 四边形的费马点 数学

2023-10-08 07:20

本文主要是介绍Fermat Point in Quadrangle POJ 3990 四边形的费马点 数学,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目:https://vjudge.net/problem/POJ-3990

题意:输入四边形四个顶点的坐标(浮点数, 0<=x, y <=1000),求一个点到这四个顶点的距离和最小,输出这个最小的距离和。

关于数据:这题数据可以极其之坑,我现在AC的代码能A掉POJ3990和UVALive5102却A不掉HDU3694,看样还有坑没补上。
说是四边形,实际样例就给了一种四个点都重合的情况,那么还有两两重合,三个重合一个不重合,四点共线。。。。。。至于两个点重合另两个点不重合的情况这就变成三角形了,连四边形的特殊情况都算不上了,同样的情况还有三点共线另一点不共线。
然后四边形分两类,凹四边形和凸四边形都要考虑。

思路:
题意所描述的点叫做费马点,四边形的费马点分两类
这里写图片描述
对于凸四边形(任意一条边都使另外两顶点在该边所在直线的同侧),费马点在两对角线交点上,以下作简单证明:
如图一,E点为对角线交点,距离和为BD + AC。任取与E不重合的点F,F到四点的距离为FD +FB +FA +FC,在三角形BDF中,DF+BF > BD , 在三角形AF+CF > AC,因此F点到顶点的距离和大于E点到各顶点的距离和,因此E点即为费马点。

对于凹多边形(存在边使另外两顶点在该边的两侧),费马点在凹顶点处,以下作简单证明。
这里写图片描述
首先证明一个引理,在凸多边形ABCD中,B为凹点,则AD+CD > AB+BC。
延长AB,交CD于E。
AD+CD = AD + DE + CE > AE + CE = AB + BE + CE > AB + BC
(连续使用三角形两边之和大于第三边)
这里写图片描述
如图二,B点为凹点,距离和为 AB+BC+BD。
考虑以下几种情况:
(1)点在凹四边形内,任取点E,距离和为AE+BE+CE+DE。
由引理可知,AE+CE > AB + BC
在三角形BDE中,BE+DE > BD 因此 AE+BE+CE+DE > AB+BC+BD。
(2)点在凹四边形外,任取点F,距离和为AF+BF+CF+DF。
显然,F在AC线段以外时,F离AC越远,距离和越大,F必定不为费马点;而F若在三角形ABC内时证明方法与(1)中相同。
(3)点在多边形的非凹顶点处,如点A,距离和为AD+AB+AC。
由引理可知AC+AD>BC+BD,因此AB+AC+AD > AB+BC+BD。
其实还有点在边上的情况,也不难证明。

具体实现思路,首先如何判断是凹四边形还是凸四边形,对凸包问题一无所知的我们队想出了一个凹四边形的特点作为区分方法。
这里写图片描述
如图四,组成凹四边形的四个顶点可以组成四个三角形,必定能找到一个三角形ADC的面积是其他三个三角形的和,而凹四边形没有这个特点。

那么接下来,如何找到对角线交点?找到两组对角的顶点算出对角线的直线方程联立求解即可解出对角线交点坐标。然而如何找到对角呢?极角排序是个好办法,可惜我当时不会,我用了这样一个特点——在凸四边形中,对角线把另外两顶点分到了对角线两侧,而边则使其他顶点在同侧。两侧和同侧的判断再次借助直线方程,将顶点坐标带入判断点在直线下侧还是上侧。

对于凹四边形,如何找到凹点呢?呃,我直接避开了这个问题,把四个顶点的距离和分别求一遍,最小的肯定是凹点。

另外有个解析几何上的坑,如果用点斜式表示直线需要时时刻刻注意斜率不存在的情况,解两直线交点时解出x需要回代解y的时候注意回代哪一条直线,因为其中一条有可能使分母为零,此时需要换另一条。

啊,这题真是做的想吐,一个数学题愣是写了两百多行代码,还因为手误一直Runtime Error死活查不出错,最后靠使劲出数据把程序测崩了才找出Bug Orz

代码:c++
这代码实在是又臭又长,由于前期想得很不周全在不断修改增补的过程中代码结构变得相当糟糕,仅作参考

#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <map>
using namespace std;const double INF = 1e15;struct Point
{double x, y;Point(double xx = 0, double yy = 0) : x(xx), y(yy) {}bool operator==(const Point &t){if (x == t.x && y == t.y){return true;}return false;}
};struct quadrangle
{Point p[10];int status;vector<pair<int, int> > pointpair;bool input(){status = 0;pointpair.clear();bool ok = true;for (int i = 0; i < 4; i++){scanf("%lf%lf", &p[i].x, &p[i].y);if (p[i].x == -1){ok = false;}}if (!ok)return ok;if (p[0] == p[1] && p[0] == p[2] && p[0] == p[3]){status = 1;return ok;}int cnt = 0;for (int i = 0; i < 4; i++){for (int j = i + 1; j < 4; j++){if (p[i] == p[j]){status = 2;cnt++;}}}if (status == 2){return ok;}for (int i = 0; i < 4; i++){double sum = 0;for (int j = 0; j < 4; j++){if (i != j){sum += area(j);}}if (area(i) == sum){status = 3;}}return ok;}void dividepoint(){if (status == 1){return;}else if (status == 2){for (int i = 1; i < 4; i++){if (!(p[0] == p[i])){vector<int> t;for (int j = 1; j < 4; j++){if (j != i){t.push_back(j);}}pointpair.push_back(make_pair(0, i));pointpair.push_back(make_pair(t[0], t[1]));return;}}}for (int i = 1; i < 4; i++){if (p[0].x - p[i].x == 0){double dx[10];int cnt = 0;for (int j = 1; j < 4; j++){if (j != i){dx[cnt++] = p[0].x - p[j].x;}}if (dx[0] * dx[1] < 0){pointpair.push_back(make_pair(0, i));vector<int> t;for (int j = 1; j < 4; j++){if (j != i){t.push_back(j);}}pointpair.push_back(make_pair(t[0], t[1]));return;}}else{double k = (p[0].y - p[i].y) / (p[0].x - p[i].x);double b = p[0].y - k*p[0].x;double dy[10];int cnt = 0;for (int j = 1; j < 4; j++){if (j != i){dy[cnt++] = k*p[j].x + b - p[j].y;}}if (dy[0] * dy[1] < 0){pointpair.push_back(make_pair(0, i));vector<int> t;for (int j = 1; j < 4; j++){if (i != j){t.push_back(j);}}pointpair.push_back(make_pair(t[0], t[1]));return;}}}}double solve(){if (status == 1){return 0;}else if (status == 2){return getanswer(p[0]);}double ans = INF;if (status == 0){double x0 = p[pointpair[0].first].x;double y0 = p[pointpair[0].first].y;double x1 = p[pointpair[0].second].x;double y1 = p[pointpair[0].second].y;double xx0 = p[pointpair[1].first].x;double yy0 = p[pointpair[1].first].y;double xx1 = p[pointpair[1].second].x;double yy1 = p[pointpair[1].second].y;double x = ((y0 - y1)*(xx0 - xx1)*x0 - (yy0 - yy1)*(x0 - x1)*xx0 + (yy0 - y0)*(x0 - x1)*(xx0 - xx1)) / ((y0 - y1)*(xx0 - xx1) - (x0 - x1)*(yy0 - yy1));double y = x0 == x1 ? ((x - xx0)*(yy0 - yy1)) / (xx0 - xx1) + yy0 : ((x - x0)*(y0 - y1)) / (x0 - x1) + y0;ans = getanswer(Point(x, y));}for (int i = 0; i < 4; i++){ans = min(ans, getanswer(p[i]));}return ans;}double getanswer(Point t){double ans = 0;for (int i = 0; i < 4; i++){ans += sqrt((p[i].x - t.x)*(p[i].x - t.x) + (p[i].y - t.y)*(p[i].y - t.y));}return ans;};double area(int u){int cnt = 0;int arr[10];for (int i = 0; i < 4; i++){if (i != u){arr[cnt++] = i;}}cnt = 0;double a, b, c;for (int i = 0; i < 3; i++){for (int j = i + 1; j < 3; j++){if (cnt == 0)a = dist(i, j);else if (cnt == 1)b = dist(i, j);else if (cnt == 2)c = dist(i, j);cnt++;}}double p = 1.0 / 2 * (a + b + c);return sqrt(p*(p - a)*(p - b)*(p - c));}double dist(int a, int b){return sqrt((p[a].x - p[b].x)*(p[a].x - p[b].x) + (p[a].y - p[b].y)*(p[a].y - p[b].y));}
};int main()
{quadrangle data;while (data.input()){data.dividepoint();printf("%.4f\n", data.solve());}return 0;
}

这篇关于Fermat Point in Quadrangle POJ 3990 四边形的费马点 数学的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/163657

相关文章

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1511 Invitation Cards(spfa最短路)

题意是给你点与点之间的距离,求来回到点1的最短路中的边权和。 因为边很大,不能用原来的dijkstra什么的,所以用spfa来做。并且注意要用long long int 来存储。 稍微改了一下学长的模板。 stack stl 实现代码: #include<stdio.h>#include<stack>using namespace std;const int M

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

poj 1502 MPI Maelstrom(单源最短路dijkstra)

题目真是长得头疼,好多生词,给跪。 没啥好说的,英语大水逼。 借助字典尝试翻译了一下,水逼直译求不喷 Description: BIT他们的超级计算机最近交货了。(定语秀了一堆词汇那就省略吧再见) Valentine McKee的研究顾问Jack Swigert,要她来测试一下这个系统。 Valentine告诉Swigert:“因为阿波罗是一个分布式共享内存的机器,所以它的内存访问

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

uva 10014 Simple calculations(数学推导)

直接按照题意来推导最后的结果就行了。 开始的时候只做到了第一个推导,第二次没有继续下去。 代码: #include<stdio.h>int main(){int T, n, i;double a, aa, sum, temp, ans;scanf("%d", &T);while(T--){scanf("%d", &n);scanf("%lf", &first);scanf