13B - Letter A
原题链接
这是一道基础的计算几何题
题意:
给你任意的三条线段的坐标,问能否组成合格的A。
这里有三条限制:
1.任意其中两条必有一个公共点,注意,一定是一个,并且没有公共点的第三条边的两个端点要在那两条有公共边的边上;
2.两条有公共点的边的夹角大于0度小于等于90度;
3.这两条边被第三条边所划分的,分别各自被划分为两条,其中较长的不能大于较短的4倍。
思路:直接按照要求进行判断,注意第三条线段的点不落在组成A的左右两条边的线段上的情况(但是落在对应的直线上)
代码如下
struct segment{
ll x1, y1, x2, y2;
}seg[3];
struct point{
ll x, y;
};
bool judge(ll x1, ll y1, ll x2, ll y2, ll x3, ll y3) //判断三点是否共线
{
return (x2 - x1) * (y3 - y2) == (y2 - y1) * (x3 - x2);
}
double dist(ll x1, ll y1, ll x2, ll y2) //计算两点距离
{
double x = x1 - x2, y = y1 - y2;
return sqrt(x * x + y * y);
}
bool inl(ll x1, ll y1, ll x2, ll y2, ll x3, ll y3) //判断 C(x3, y3)是否在线段内
{
ll xa = x3 - x1, xb = x3 - x2;
ll ya = y3 - y1, yb = y3 - y2;
if(xa * xb > 0 || ya * yb > 0) return false;
return true;
}
int main()
{
IOS;
ll T;
cin >> T;
while(T --)
{
for(ll i = 0 ; i < 3 ; i ++)
cin >> seg[i].x1 >> seg[i].y1 >> seg[i].x2 >> seg[i].y2;
ll a = -1, b = -1, c = -1;
//找到有公共点的两个线段
for(ll i = 0 ; i < 3 ; i ++)
for(ll j = i + 1 ; j < 3 ; j ++)
{
if(seg[i].x1 == seg[j].x1 && seg[i].y1 == seg[j].y1)
a = i, b = j;
else if(seg[i].x1 == seg[j].x2 && seg[i].y1 == seg[j].y2)
a = i, b = j;
else if(seg[i].x2 == seg[j].x1 && seg[i].y2 == seg[j].y1)
a = i, b = j;
else if(seg[i].x2 == seg[j].x2 && seg[i].y2 == seg[j].y2)
a = i, b = j;
}
if(a == -1 || b == -1)
{
cout << "NO\n";
continue;
}
//应用余弦定理计算角度
double angle, s, maxd, mind;
double d1 = dist(seg[a].x1, seg[a].y1, seg[a].x2, seg[a].y2);
double d2 = dist(seg[b].x1, seg[b].y1, seg[b].x2, seg[b].y2);
if(seg[a].x1 == seg[b].x1 && seg[a].y1 == seg[b].y1)
s = (seg[a].x2 - seg[a].x1) * (seg[b].x2 - seg[b].x1) + (seg[a].y2 - seg[a].y1) * (seg[b].y2 - seg[b].y1);
else if(seg[a].x1 == seg[b].x2 && seg[a].y1 == seg[b].y2)
s = (seg[a].x2 - seg[a].x1) * (seg[b].x1 - seg[b].x2) + (seg[a].y2 - seg[a].y1) * (seg[b].y1 - seg[b].y2);
else if(seg[a].x2 == seg[b].x1 && seg[a].y2 == seg[b].y1)
s = (seg[a].x1 - seg[a].x2) * (seg[b].x2 - seg[b].x1) + (seg[a].y1 - seg[a].y2) * (seg[b].y2 - seg[b].y1);
else s = (seg[a].x1 - seg[a].x2) * (seg[b].x1 - seg[b].x2) + (seg[a].y1 - seg[a].y2) * (seg[b].y1 - seg[b].y2);
//计算出夹角
angle = acos(s / (d1 * d2));
if(angle > pi / 2.0 || angle <= 0)
{
cout << "NO\n";
continue;
}
// 确定第三条边
for(ll i = 0 ; i < 3 ; i ++)
if(i != a && i != b)
c = i;
//确定第三条线段在 a 和 b上的点 pa 和 pb
point pa = {mod, mod}, pb = {mod, mod};
if(judge(seg[a].x1, seg[a].y1, seg[a].x2, seg[a].y2, seg[c].x1, seg[c].y1))
pa = {seg[c].x1, seg[c].y1};
else if(judge(seg[a].x1, seg[a].y1, seg[a].x2, seg[a].y2, seg[c].x2, seg[c].y2))
pa = {seg[c].x2, seg[c].y2};
if(judge(seg[b].x1, seg[b].y1, seg[b].x2, seg[b].y2, seg[c].x1, seg[c].y1))
pb = {seg[c].x1, seg[c].y1};
else if(judge(seg[b].x1, seg[b].y1, seg[b].x2, seg[b].y2, seg[c].x2, seg[c].y2))
pb = {seg[c].x2, seg[c].y2};
bool flag = true;
if(pa.x == pb.x && pa.y == pb.y) flag = false;
if(pa.x == mod || pa.y == mod || pb.x == mod || pb.y == mod) flag = false;
// 计算分割的两部分是否不满足配比
d1 = dist(pa.x, pa.y, seg[a].x1, seg[a].y1);
d2 = dist(pa.x, pa.y, seg[a].x2, seg[a].y2);
maxd = max(d1, d2);
mind = min(d1, d2);
if(mind * 4 < maxd) flag = false;
d1 = dist(pb.x, pb.y, seg[b].x1, seg[b].y1);
d2 = dist(pb.x, pb.y, seg[b].x2, seg[b].y2);
maxd = max(d1, d2);
mind = min(d1, d2);
if(mind * 4 < maxd) flag = false;
//如果点不在线段内
if(!inl(seg[a].x1, seg[a].y1, seg[a].x2, seg[a].y2, pa.x, pa.y)) flag = false;
if(!inl(seg[b].x1, seg[b].y1, seg[b].x2, seg[b].y2, pb.x, pb.y)) flag = false;
if(!flag) cout << "NO\n";
else cout << "YES\n";
}
return 0;
}