完美解法的两道题

本文提供两道编程题目的最优解法,包括如何判断N个整数点是否共圆的问题及股神问题中寻找最优解的算法。通过对托勒密定理的应用和下降日期的规律分析,实现高效准确的解答。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

近些天看到了两道题,也做了,不断修改和完善,觉得自己这两道题的解法是最优的,不知你们同意不,如果还有更好的解法,请在评论区留言.

第一题:N点共圆

题目:

给你N个整数点,判断这N个是否可能在同一个圆周上.

输入:

多组输入,第一行输入一个整数N(1<=N<=9),第二行输入2N个整数,表示N个(x,y)的点,-20 <=x,y<=20.而且给出点是逆时针给出,任意三点不共线。

输出:

如果N个点可能在同一个圆上,输出”Yes”,否则输出”No”.

样例输入:

3
000340
2
1010101

样例输出:

Yes
Yes

解析:

直接解法:浮点数模拟,先利用三点求出圆心坐标和半径,再判断其余的点时候在圆上,可以通过这个题,但是误差比较大,浮点数模拟必然会出现误差.大家普遍都是这个解法。
 
完美解法:由于给出的点是整点,且逆时针给出,那么我们就要想办法规避浮点数的误差,先选出三个点,因为三点确定一个圆,遍历其余的点,利用圆内四边形的判定定理:托勒密定理,判断这四个点组成的四边形是否为圆内四边形,如果其余的点与先前选出的三个点都可以组成圆内四边形,那么这N个点就在同一个圆上;判定条件:

AB¯¯¯¯¯×CD¯¯¯¯¯+AD¯¯¯¯¯×BC¯¯¯¯¯==AC¯¯¯¯¯×BD¯¯¯¯¯

两边同时平方得到:
AB¯¯¯¯¯2×CD¯¯¯¯¯2+AD¯¯¯¯¯2×BC¯¯¯¯¯2+2×AB¯¯¯¯¯×CD¯¯¯¯¯×AD¯¯¯¯¯×BC¯¯¯¯¯==AC¯¯¯¯¯2×BD¯¯¯¯¯2

移项再同时平方得到:
(AB¯¯¯¯¯2×CD¯¯¯¯¯2+AD¯¯¯¯¯2×BC¯¯¯¯¯2AC¯¯¯¯¯2×BD¯¯¯¯¯2)2==4×AB¯¯¯¯¯2×CD¯¯¯¯¯2×AD¯¯¯¯¯2×BC¯¯¯¯¯2

这样子,开根号的浮点运算也避免了,用这个等式可以完美判断.

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

struct Point {
    LL x, y;
};

LL pow2(LL x)
{
    return x * x;
}

LL dis2(Point a, Point b)
{
    return pow2(a.x - b.x) + pow2(a.y - b.y);
}

int main()
{
    int n;
    while (cin >> n) {
        vector<Point> points(n);
        for (int i = 0; i < n; i++)
            cin >> points[i].x >> points[i].y;
        if (n < 4) {
            cout << "Yes" << endl;
            continue;
        }
        bool f = true;
        for (int i = 3; i < n; i++) {
            if (pow2(dis2(points[0], points[1]) * dis2(points[2], points[i]) +
                     dis2(points[1], points[2]) * dis2(points[0], points[i]) -
                     dis2(points[1], points[i]) * dis2(points[0], points[2])) !=
                    4 * dis2(points[0], points[1]) * dis2(points[2], points[i]) *
                    dis2(points[1], points[2]) * dis2(points[0], points[i])) {
                f = false;
                break;
            }
        }
        cout << (f ? "Yes" : "No") << endl;
    }
}

第二题:股神

题目:

这是赛码网上的一个题,有人问我,我就做了下,题目链接:[点这里].

解析:

普通解法我就不赘述了,很好搞,模拟就是了.

完美解法:首先我们观察下哪一天会下降:

12345678910bk

我们挑出下降的日子: 3 6 10  bk  , b1=3 ,那么这些下降的日子是否满足通项公式呢,满足的: bk=bk1+k+1 ,通过迭代可以求出:
bk=(k+4)(k1)2+3

给出某一天 n ,我们可以求出最靠近n的下降日子,这里可以通过遍历,也可以通过二分来求,遍历求法复杂度为 O(n) ,二分复杂度为 O(log2n) ,待会分别给出代码;

求出了 k ,那么bk天的数字我们就知道了:

abk=1+(1+2++k)k=1+(k+1)k2k

那么,在剩下的日子里,只会上涨,不会下降,因此:
ans=abk+nbk

因此,完美解法的时间复杂度为 O(log2n) .

代码:

O(log2n) :

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

int main()
{
    LL n;
    while (cin >> n) {
        LL x = 0, y = n;
        while (x < y) {
            LL mid = (x + y + 1) / 2;
            (mid + 4) * (mid - 1) / 2 + 3 <= n ? x = mid : y = mid - 1;
        }
        LL ans = 1 + (x + 1) * x / 2 - x;
        LL res = n - (x + 3) * x / 2 - 1;
        ans += res;
        cout << ans << endl;
    }
}

O(n) :

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

int main()
{
    LL n;
    while (cin >> n) {
        LL x = 0;
        for (; (x + 4) * (x - 1) / 2 + 3 <= n; x++);
        --x;
        LL ans = 1 + (x + 1) * x / 2 - x;
        LL res = n - (x + 3) * x / 2 - 1;
        ans += res;
        cout << ans << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值