三角形适合另一个三角形
给定 2 个三角形的边长。确定第二个三角形是否可以放入第一个三角形内?
有关更多详细信息,请阅读下面的完整问题陈述:
http ://acm.timus.ru/problem.aspx?space=1&num=1566&locale=en
我下面的实现尝试了所有 (3!)^2 种可能的组合对齐三角形的底边。然后,它尝试将第二个三角形移动到第一个三角形内,同时检查第二个三角形的底边是否超过第一个三角形的底边。
但我不断收到错误答案(WA)#16。
我给出的案例是第二张图像。很明显,如果旋转 PQR 来对齐长度为 2.77 和 3.0 的边,则第三个顶点将不会位于三角形 ABC 内部。长度为 4.2 的边只能沿着透镜 5 的边对齐。因此,这种情况仅在第二张图像所示的配置中得到满足。
你能帮我找到错误,建议一些我的算法崩溃的测试用例吗?也欢迎替代算法。
#include <cmath>
#include <iostream>
using namespace std;
const double PI = atan(1.0)* 4;
// Traingle ABC (Envelope)
double xa, ya, xb, yb, xc, yc;
// Traingle PQR (Postcard)
double xp, yp, xq, yq, xr, yr;
// Angle between sides AB and AC
double theta;
double signWrtLine(double x1, double y1, double x2, double y2, double x, double y)
{
double A = y2 - y1;
double B = x1 - x2;
double C = -(A * x1 + B * y1);
return (A * x + B * y + C);
}
bool fit()
{
if ((xr > xc) || (yq > yb)) return false;
if (signWrtLine(xa, ya, xb, yb, xq, yq) < 0) {
double d = (yq / tan(theta)) - xq;
return (xr + d <= xc);
}
return (signWrtLine(xa, ya, xb, yb, xq, yq) >= 0 &&
signWrtLine(xb, yb, xc, yc, xq, yq) >= 0 &&
signWrtLine(xc, yc, xa, ya, xq, yq) >= 0);
}
bool fit(double a[], double b[])
{
// generate the 3! permutations of the envelope
// loops i,k
for (int i = 0; i < 3; i++) {
double angle;
double u = a[i], v = a[(i + 1) % 3], w = a[(i + 2) % 3];
for (int k = 0; k < 2; k++) {
switch (k) {
case 0:
xa = 0, ya = 0;
angle = theta = acos((u * u + v * v - w * w) / (2 * u * v));
xb = v * cos(angle), yb = v * sin(angle);
xc = u, yc = 0;
break;
case 1:
// reflect envelope
swap(v, w);
angle = theta = acos((u * u + v * v - w * w) / (2 * u * v));
xb = v * cos(angle), yb = v * sin(angle);
break;
}
// generate the 3! permutations of the postcard
// loops j,k
for (int j = 0; j < 3; j++) {
double angle;
double u = b[j], v = b[(j + 1) % 3], w = b[(j + 2) % 3];
for (int k = 0; k < 2; k++) {
switch (k) {
case 0:
xp = 0, yp = 0;
angle = acos((u * u + v * v - w * w) / (2 * u * v));
xq = v * cos(angle), yq = v * sin(angle);
xr = u, yr = 0;
break;
case 1:
// reflect postcard
swap(v, w);
angle = acos((u * u + v * v - w * w) / (2 * u * v));
xq = v * cos(angle), yq = v * sin(angle);
break;
}
if (fit()) return true;
}
}
}
}
return false;
}
int main()
{
double a[3], b[3];
for (int i = 0; i < 3; i++) cin >> a[i];
for (int i = 0; i < 3; i++) cin >> b[i];
if(fit(a, b)) cout << "YES" << endl;
else cout << "NO" << endl;
return 0;
}
Given the lengths of the sides of 2 triangles. Determine if the second triangle can fit inside the first triangle?
For more detailed info read the full problem statement below:
http://acm.timus.ru/problem.aspx?space=1&num=1566&locale=en
My implementation below tries all the (3!)^2 possible combinations of aligning the bases of the triangles. It then tries to shift the second triangle inside the first triangle while checking that the base of the second triangle doesn't exceed the base of the first triangle.
But I keep getting Wrong Answer(WA) #16.
The case I gave is the second image. It is obvious that if you rotate PQR to align the sides of length 2.77 and 3.0 the third vertex will not be inside triangle ABC. The side of length 4.2 can only be aligned along the side of len 5. Thus this case is satisfied only in the configuration show in the second image.
Can you help me find the bug, suggest some test cases where my algorithm breaks down. Alternative algorithms are also welcome.
#include <cmath>
#include <iostream>
using namespace std;
const double PI = atan(1.0)* 4;
// Traingle ABC (Envelope)
double xa, ya, xb, yb, xc, yc;
// Traingle PQR (Postcard)
double xp, yp, xq, yq, xr, yr;
// Angle between sides AB and AC
double theta;
double signWrtLine(double x1, double y1, double x2, double y2, double x, double y)
{
double A = y2 - y1;
double B = x1 - x2;
double C = -(A * x1 + B * y1);
return (A * x + B * y + C);
}
bool fit()
{
if ((xr > xc) || (yq > yb)) return false;
if (signWrtLine(xa, ya, xb, yb, xq, yq) < 0) {
double d = (yq / tan(theta)) - xq;
return (xr + d <= xc);
}
return (signWrtLine(xa, ya, xb, yb, xq, yq) >= 0 &&
signWrtLine(xb, yb, xc, yc, xq, yq) >= 0 &&
signWrtLine(xc, yc, xa, ya, xq, yq) >= 0);
}
bool fit(double a[], double b[])
{
// generate the 3! permutations of the envelope
// loops i,k
for (int i = 0; i < 3; i++) {
double angle;
double u = a[i], v = a[(i + 1) % 3], w = a[(i + 2) % 3];
for (int k = 0; k < 2; k++) {
switch (k) {
case 0:
xa = 0, ya = 0;
angle = theta = acos((u * u + v * v - w * w) / (2 * u * v));
xb = v * cos(angle), yb = v * sin(angle);
xc = u, yc = 0;
break;
case 1:
// reflect envelope
swap(v, w);
angle = theta = acos((u * u + v * v - w * w) / (2 * u * v));
xb = v * cos(angle), yb = v * sin(angle);
break;
}
// generate the 3! permutations of the postcard
// loops j,k
for (int j = 0; j < 3; j++) {
double angle;
double u = b[j], v = b[(j + 1) % 3], w = b[(j + 2) % 3];
for (int k = 0; k < 2; k++) {
switch (k) {
case 0:
xp = 0, yp = 0;
angle = acos((u * u + v * v - w * w) / (2 * u * v));
xq = v * cos(angle), yq = v * sin(angle);
xr = u, yr = 0;
break;
case 1:
// reflect postcard
swap(v, w);
angle = acos((u * u + v * v - w * w) / (2 * u * v));
xq = v * cos(angle), yq = v * sin(angle);
break;
}
if (fit()) return true;
}
}
}
}
return false;
}
int main()
{
double a[3], b[3];
for (int i = 0; i < 3; i++) cin >> a[i];
for (int i = 0; i < 3; i++) cin >> b[i];
if(fit(a, b)) cout << "YES" << endl;
else cout << "NO" << endl;
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
重心坐标!详细来说:
设“包络”三角形有顶点A、B、C;不失一般性,您可以将顶点 A 放置在原点并将边 AB 与 +x 轴对齐。使用包络三角形的边长求出顶点 A 处的角度,即边 AB 和 AC 之间的角度。使用这个角度,您可以定义一个新的坐标系(u,v);在此坐标系中,顶点坐标为 A=(0,0)、B=(1,0) 和 C=(0,1)。
现在,取另一个具有顶点 A'、B'、C' 的三角形,并首先找到以下每种情况的 3 个顶点的 XY 坐标: (A'B', B'C', A'C') 与+x坐标轴。对于每个此类对齐,将其他两个顶点变换到由包络三角形确定的 UV 坐标系。如果碰巧其他两个顶点的 (u,v) 坐标为 0 <= u,v <= 1 并且 u+v<=1,则该三角形适合包络三角形。
平面三角形的两条边之间的夹角可以通过正弦定理得到;不过,如果顶点处的角度是钝角(> PI/2),则必须小心一点,因为正弦函数在区间 [0,PI] 上围绕 PI/2 对称。要检查角度是否是钝角,您还需要使用余弦定理,尽管您不需要计算余弦本身: if |AB|^2 + |AC|^2 > |BC|^2,A 处的角是钝角。
我想这就是总结。
Barycentric coordinates! In detail:
Let the "envelope" triangle have vertices A, B, C; without loss of generality you can place vertex A at the origin and align the side AB with the +x axis. Use the edge lengths of the envelope triangle to find the angle at vertex A, i.e., the angle between the sides AB and AC. Using this angle, you define a new coordinate system (u,v); in this coordinate system the vertex coordinates are A=(0,0), B=(1,0) and C=(0,1).
Now, take the other triangle with vertices A',B',C', and find first the XY coordinates of the 3 vertices for each case of: (A'B', B'C', A'C') aligned with +x coordinate axis. For each such alignment, transform the other two vertices to the UV-coordinate system determined by the envelope triangle. If it happens that both of the other vertices have (u,v) coordinates with 0 <= u,v <= 1 with u+v<=1, the triangle fits within the envelope triangle.
Angle between two sides can be obtained through the sine theorem for planar triangles; though you have to be a bit careful if the angle at a vertex is obtuse (> PI/2) since the sine function is symmetric around PI/2 on the interval [0,PI]. To check whether the angle is obtuse, you also need to use the cosine theorem, though you don't need to calculate the cosine itself: if |AB|^2 + |AC|^2 > |BC|^2, the angle at A is obtuse.
I think that about sums it up.
比较双精度数时使用 epsilon (1e-10)!
Use epsilon (1e-10) when comparing doubles!
可以从这里尝试 - 链接。到目前为止,问题似乎尚未解决,因此最好采用一些启发式方法来获得简单的情况(例如检查内切/外接圆、对齐边界等)并希望获得最好的结果。
Might try from here - Link. It seems that the problem is unsolved so far, so best bet to go with some heuristics to get simple cases (like checks for inscribed / circumscribed circles, aligning borders, etc) and hope for the best.