Operation Love(数学)

304 阅读2分钟

C-Operation Love_2023牛客五一集训派对day3 (nowcoder.com)

题目描述

给出一只右手的平面图,左手与右手对称。

接下来将右手或左手放到平面坐标系,并给出这只手上的二十个点的坐标,要求你判断出这只手是右手还是左手。

注意,规定手背面朝上,不能翻转可以旋转,不能放大缩小。

image.png

题目分析

说一下我当时的解题思路。

首先确定从图形的特征分析,确定一只手是左手还是右手不需要那么多点,例如对上如手的坐标进行分析,我以左下角 (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;
}