BestCoder-Round#38

1001

描述

  • 给出四个三维坐标下的点, 判定是否为正方形.

分析

  • 用向量的数量积来判定是否垂直, 再判断长度.
  • 我是在纸上画出了 A(3,2)=6 种情况然后暴力枚举判断是否为正方形. 组合数的意义表示在 2、3、4 三个点中有序选出两个点和 1 相邻.
  • 我写了一百多行的代码, 看了看别人简洁的代码, 发现可以直接用STL的 next_permutation 来生成下一个排列, 把排列看做从左上角开始的顺时针 (或逆时针) 四个点的编号, 就可以少打很多暴力枚举.
    • 尤其是在点数更多时这个办法更有用.
  • 然后就去看题解, 发现可以直接用三个点来判断, 用向量法推出第四个点的坐标, 判定是否符合.
  • 计算几何的一些常用函数还是打成函数比较好, 可以降低代码复杂度.

代码

https://code.csdn.net/snippets/647464

#include <cstdio>
using namespace std;
typedef long long lli;

struct Vector {
    lli x, y, z;

    Vector(lli x=0, lli y=0, lli z=0) : x(x), y(y), z(z) {}

    void init() {
        scanf("%lld%lld%lld", &x, &y, &z);
    }
};

Vector operator - (Vector A, Vector B) {
    return Vector(A.x-B.x, A.y-B.y, A.z-B.z);
}

lli Dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y + A.z*B.z; }
lli Length2(Vector A) { return Dot(A, A); }


int main() {
    int T;
    scanf("%d", &T);
    Vector p1, p2, p3, p4;
    Vector u1, u2, u3, u4;
    Vector v1, v2, v3, v4;

    for(int kase = 1; kase <= T; kase++) {
        printf("Case #%d: ", kase);
        p1.init(); p2.init(); p3.init(); p4.init();

        u1 = p2-p1; v1 = p3-p1;
        u2 = p1-p2; v2 = p4-p2;
        u3 = p2-p4; v3 = p3-p4;
        u4 = p1-p3; v4 = p4-p3;
        if(Dot(u1, v1) == 0 && Dot(u2, v2) == 0 && Dot(u3, v3) == 0 && Dot(u4, v4) == 0) {
            if(Length2(u1) == Length2(v1) && Length2(u2) == Length2(v2) && Length2(u3) == Length2(v3) && Length2(u4) == Length2(v4)) puts("Yes");
            else puts("No");

            continue;
        }

        u1 = p2-p1; v1 = p4-p1;
        u2 = p1-p2; v2 = p3-p2;
        u3 = p2-p3; v3 = p4-p3;
        u4 = p1-p4; v4 = p3-p4;
        if(Dot(u1, v1) == 0 && Dot(u2, v2) == 0 && Dot(u3, v3) == 0 && Dot(u4, v4) == 0) {
            if(Length2(u1) == Length2(v1) && Length2(u2) == Length2(v2) && Length2(u3) == Length2(v3) && Length2(u4) == Length2(v4)) puts("Yes");
            else puts("No");

            continue;
        }

        u1 = p3-p1; v1 = p2-p1;
        u2 = p1-p3; v2 = p4-p3;
        u3 = p3-p4; v3 = p2-p4;
        u4 = p1-p2; v4 = p4-p2;
        if(Dot(u1, v1) == 0 && Dot(u2, v2) == 0 && Dot(u3, v3) == 0 && Dot(u4, v4) == 0) {
            if(Length2(u1) == Length2(v1) && Length2(u2) == Length2(v2) && Length2(u3) == Length2(v3) && Length2(u4) == Length2(v4)) puts("Yes");
            else puts("No");

            continue;
        }

        u1 = p3-p1; v1 = p4-p1;
        u2 = p1-p3; v2 = p2-p3;
        u3 = p3-p2; v3 = p4-p2;
        u4 = p1-p4; v4 = p2-p4;
        if(Dot(u1, v1) == 0 && Dot(u2, v2) == 0 && Dot(u3, v3) == 0 && Dot(u4, v4) == 0) {
            if(Length2(u1) == Length2(v1) && Length2(u2) == Length2(v2) && Length2(u3) == Length2(v3) && Length2(u4) == Length2(v4)) puts("Yes");
            else puts("No");

            continue;
        }

        u1 = p4-p1; v1 = p2-p1;
        u2 = p1-p4; v2 = p3-p4;
        u3 = p4-p3; v3 = p2-p3;
        u4 = p1-p2; v2 = p3-p2;
        if(Dot(u1, v1) == 0 && Dot(u2, v2) == 0 && Dot(u3, v3) == 0 && Dot(u4, v4) == 0) {
            if(Length2(u1) == Length2(v1) && Length2(u2) == Length2(v2) && Length2(u3) == Length2(v3) && Length2(u4) == Length2(v4)) puts("Yes");
            else puts("No");

            continue;
        }

        u1 = p4-p1; v1 = p3-p1;
        u2 = p1-p4; v2 = p2-p4;
        u3 = p3-p2; v3 = p4-p2;
        u4 = p1-p3; v2 = p2-p3;
        if(Dot(u1, v1) == 0 && Dot(u2, v2) == 0 && Dot(u3, v3) == 0 && Dot(u4, v4) == 0) {
            if(Length2(u1) == Length2(v1) && Length2(u2) == Length2(v2) && Length2(u3) == Length2(v3) && Length2(u4) == Length2(v4)) puts("Yes");
            else puts("No");

            continue;
        }

        puts("No");
    }
    return 0;
}

1002

描述

  • 在数组 a 中找出两个数 ai, aj (ij) , 使得 gcd(ai,aj) 取到最大值.
  • 数组元素个数 n50000

分析

  • 因为数组是给出的, 有不确定性, 所以放弃了找规律.
  • 暴力拆解因数, 用 hash 表来存储这个因数有没有出现过. 如果这个因数出现过就更新答案, 否则标记为已出现.
  • 枚举因数肯定是 [1,n] 之内的, 同时如果 i x 的因数, xi 也是 x 的因数, 如果 xi 出现过, 那么就可以不再继续枚举了. 因为随着 i 的增大, xi 会减小, 不会比现在答案更优.
  • O(nn)
  • 还可以加一个优化, 就是把 a 数组 sort 后从大往小来判定. 这样提前搜到答案的几率或许更大一些.

代码

https://code.csdn.net/snippets/647465

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 10;

bool factor[maxn];
int c[maxn];

int main() {
    int T;
    scanf("%d", &T);
    for(int kase = 1; kase <= T; kase++) {
        int n, ans = 1;
        scanf("%d", &n);
        memset(factor, 0, sizeof(factor));
        for(int i = 0; i < n; i++) scanf("%d", &c[i]);
        sort(c, c + n);

        for(int i = n-1; i >= 0; i--) {
            int x = c[i];
            for(int k = 1; k*k <= x; k++)
                if(x % k == 0) {
                    if(ans >= x/k) break;
                    if(factor[k]) ans = max(ans, k); else factor[k] = 1;
                    if(k*k == x) continue;
                    if(factor[x/k]) { ans = x/k; break; } else factor[x/k] = 1;
                } else if(ans >= x/k) break;
        }
        printf("Case #%d: %d\n", kase, ans);
    }
    return 0;
}

1003

待补

1004

描述

小F今天3岁生日,他爸爸送给他一套魔法积木作为生日礼物,积木一共包含n块,编号从1到n,每块积木都可以任意设置高度为整数h(1≤h≤m)。小F非常开心地玩起了积木,并按照以下步骤进行操作

(1) 首先,拿出编号为1的积木,任意设置高度为h1,放在第一行行首处。
(2) 当进行到第i步时(假设前i−1块积木已经摆放成r行):拿出编号为i的积木,这时有两种放法:
(I) 任意设置积木i的高度,然后把它放在r+1行行首(产生新的一行)
(II)从r行中任意选择一行,任意设置积木i的高度hi使得hi不低于被选行最后一个积木高度,然后将积木i放在该行的最后
(3) n块积木都使用后操作结束,形成了一个图案

小F非常好奇,所有可能的操作下会形成多少种不同的图案?对于两种图案,如果它们积木总行数相同,并且对应行总积木数相同,并且对应位置的编号高度都相同,两种图案便被视为相同。

分析

  • 看题解、补题解.
  • 动态规划
  • f[i] 表示前 i 个积木的图案数.
  • f[0]=1
  • 接下来看由 f[j] 如何得到 f[i] .
    • 假设已经排好 j 个积木, 剩余 ij 个积木, 为了不重复, 把它们全部放在新的一行. 假设 s[i] 表示 h[i]h[i1] , 也就是差分. 规定 h[0]=1,h[ij+1]=m . 则 ij+1i=1si=m1 , 求正整数解就可以限定 h 的取值在 [1,m] 之间. 正整数解的个数为 (ij+1+m11ij+11)=(ij+m1ij) .
    • 看题解还发现一个细节, 就是先拿出第一个积木来. 这样可以保证该积木不会包含在那 j 个积木中, 只能在新的一行中. 保证不会出现重复.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值