题解 | #最大缘#

最大缘

https://ac.nowcoder.com/acm/contest/102256/J

首先别用python太慢了

数学题:外接圆半径

首先外接圆半径可以用海伦公式计算。

double calculate_radius(int x1, int y1, int x2, int y2, int x3, int y3) {
    // 计算三条边的长度
    double a = sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
    double b = sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
    double c = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    
    // 检查三角形是否合法
    if (a + b <= c || b + c <= a || a + c <= b) {
        return 0;
    }
    
    // 使用海伦公式计算面积
    double s = (a + b + c) / 2.0;
    double area = 0;
    // 注意:当面积接近0时可能会出现计算误差
    if (s * (s - a) * (s - b) * (s - c) < 1e-10) {
        return 0;
    }
    area = sqrt(s * (s - a) * (s - b) * (s - c));
    if (area < 1e-10) {
        return 0;
    }
    double radius = (a * b * c) / (4.0 * area);
    return radius;
}

最优解在什么情况取得?

学过曲率半径的都应该知道。 如果这三个点几乎成一条直线,那么半径肯定会非常大,那么就可以利用这个特性了。 固定A(a,b)为矩形左下角的点,变化点B(c,d)。然后可以通过这两点计算出一个直线的方程。然后以整数x遍历横坐标从a->c。对于每一个x计算直线对应的y然后向上取整(因为你只需要遍历尽可能接近直线的点,这样三个点才更像在一条直线上)那么就得到了第三个点c的坐标,计算这三点确定的外接圆半径,如果更大就试图更新最大值。一轮搜索后。B开始向A点移动,移动方式为B(a-1,c),B(a,c-1)B(a-1,c-1)然后B(a-2,c),B(a-2,c-1),B(a-2,c-2),B(a-1,c-2),B(a,c-2)
然后可以了。这里其实还有优化的空间,因为如果点B后面移动时落在了之前遍历过的直线上那么这种情况可以直接舍弃。但是既然都AC了那就不管了 alt

AC代码

// 思路是自己想的,代码是GPT写的
#include <iostream>
#include <cmath>
#include <limits>
#include <tuple>
using namespace std;

double calculate_radius(int x1, int y1, int x2, int y2, int x3, int y3) {
    // 计算三条边的长度
    double a = sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
    double b = sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
    double c = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    
    // 检查三角形是否合法
    if (a + b <= c || b + c <= a || a + c <= b) {
        return 0;
    }
    
    // 使用海伦公式计算面积
    double s = (a + b + c) / 2.0;
    double area = 0;
    // 注意:当面积接近0时可能会出现计算误差
    if (s * (s - a) * (s - b) * (s - c) < 1e-10) {
        return 0;
    }
    area = sqrt(s * (s - a) * (s - b) * (s - c));
    if (area < 1e-10) {
        return 0;
    }
    double radius = (a * b * c) / (4.0 * area);
    return radius;
}

// 根据两点和 x 坐标计算第三个点的 y 坐标,返回是否计算成功
bool get_third_point(int x1, int y1, int x2, int y2, int x, int &yResult) {
    if (x2 == x1) {
        return false;
    }
    double k = double(y2 - y1) / double(x2 - x1);
    double b = y1 - k * x1;
    double y = k * x + b;
    yResult = int(ceil(y));
    return true;
}

tuple<int, int, int, int, int, int> find_max_radius_points(int a, int b, int c, int d) {
    double max_radius = 0;
    // 默认选择三点:(a,c), (a,d), (b,c)
    int bestAx = a, bestAy = c, bestBx = a, bestBy = d, bestCx = b, bestCy = c;
    
    // 固定A为左下角点
    int x1 = a, y1 = c;
    
    // 遍历B点的可能位置
    for (int x2 = a; x2 <= b; x2++) {
        for (int y2 = c; y2 <= d; y2++) {
            // 排除与A重合的情况
            if (x2 == x1 && y2 == y1) continue;
            
            // 对于每个可能的第三个点通过 x 坐标计算 y 坐标
            for (int x3 = a; x3 <= b; x3++) {
                // 排除与A或B重合的情况
                if (x3 == x1 || x3 == x2) continue;
                int y3;
                if (!get_third_point(x1, y1, x2, y2, x3, y3)) continue;
                if (y3 < c || y3 > d) continue;
                
                // 检查三点是否共线(利用向量叉乘判断)
                double cross = fabs((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1));
                if (cross < 1e-10) continue;
                
                double r = calculate_radius(x1, y1, x2, y2, x3, y3);
                if (r > max_radius) {
                    max_radius = r;
                    bestAx = x1; bestAy = y1;
                    bestBx = x2; bestBy = y2;
                    bestCx = x3; bestCy = y3;
                }
            }
        }
    }
    return make_tuple(bestAx, bestAy, bestBx, bestBy, bestCx, bestCy);
}

int main() {
    int a, b, c, d;
    if(!(cin >> a >> b >> c >> d)) {
        cout << "输入格式错误" << endl;
        return 1;
    }
    
    if (a > b) swap(a, b);
    if (c > d) swap(c, d);
    
    if (!( (0 <= b - a && b - a <= 1000) && (0 <= d - c && d - c <= 1000) )) {
        cout << "输入范围应在1000以内" << endl;
        return 1;
    }
    
    auto points = find_max_radius_points(a, b, c, d);
    int ax, ay, bx, by, cx, cy;
    tie(ax, ay, bx, by, cx, cy) = points;
    
    cout << ax << " " << ay << "\n";
    cout << bx << " " << by << "\n";
    cout << cx << " " << cy << "\n";
    
    return 0;
}
全部评论

相关推荐

06-08 22:25
门头沟学院 Java
从零开始的转码生活:这hr不会打开手机不分青红皂白给所有人群发这句话,过一会再给所有人再发一遍,这肯定会有重复的,不管,再过一会再发一遍
点赞 评论 收藏
分享
头顶尖尖的程序员:我也是面了三四次才放平心态的。准备好自我介绍,不一定要背熟,可以记事本写下来读。全程控制语速,所有问题都先思考几秒,不要急着答,不要打断面试官说话。
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务