C-Operation Love_2023牛客五一集训派对day3 (nowcoder.com)
题目描述
给出一只右手的平面图,左手与右手对称。
接下来将右手或左手放到平面坐标系,并给出这只手上的二十个点的坐标,要求你判断出这只手是右手还是左手。
注意,规定手背面朝上,不能翻转可以旋转,不能放大缩小。
题目分析
说一下我当时的解题思路。
首先确定从图形的特征分析,确定一只手是左手还是右手不需要那么多点,例如对上如手的坐标进行分析,我以左下角 (1,0) 和 右上角 10,8 做一条以 1,0 为起点的射线,接下来我们发现 (10,0) 这个点位于射线的右侧,进而我们联想到,若当前手为左手,则相对应的这个特征点应该在相对应射线的左侧。于是我们便得到了判断左右手的方法。
第一个步骤,找到射线上两点。由图像我们发现,射线上的特征点之间的距离只有这两个点具有,因此我们通过枚举所有点,并枚举当前点到其他点的距离,若这个距离符合则将这两个点标记下来。接下来我们也可以通过只有左下角 (10,0) 这个点与 (1,0) 这个点的距离唯一来判断两个射线的起始位置。
第二个步骤,判断特征点位于射线的哪一侧。这里我便用到了数学上叉乘的方法,具体原理不再过多解释。实现方法为以射线的向量与射线起点与特征点的向量做叉乘,若为正则特征点在向量左侧为左手,若为负则特征点在向量右侧为右手。
Accept代码
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
const int N = 20;
const double eps = 1e-4;
pair<double, double> p[N];
int check(int u, double d)
{
int flg = -1;
double x1 = p[u].fi, y1 = p[u].se;
for (int i = 0; i < N; i ++)
{
double x2 = p[i].fi, y2 = p[i].se;
if (fabs((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) - d) < eps)
{
flg = i;
break;
}
}
return flg;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while (t --)
{
for (int i = 0; i < N; i ++) cin >> p[i].fi >> p[i].se;
int a, b;
for (int i = 0; i < N; i ++)
{
b = check(i, 64 + 81);
if (b != -1)
{
a = i;
break;
}
}
int c = check(a, 81);
if (c == -1) c = check(b, 81), swap(a, b);
double x1 = p[b].fi - p[a].fi, y1 = p[b].se - p[a].se;
double x2 = p[c].fi - p[a].fi, y2 = p[c].se - p[a].se;
if (x1 * y2 - x2 * y1 > 0) cout << "left\n";
else cout << "right\n";
}
return 0;
}