完美解法的两道题

来源:互联网 发布:mysql 多字段排序 编辑:程序博客网 时间:2024/06/06 00:12

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

第一题: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;    }}
原创粉丝点击