1001
描述
- 给出四个三维坐标下的点, 判定是否为正方形.
分析
- 用向量的数量积来判定是否垂直, 再判断长度.
- 我是在纸上画出了 A(3,2)=6 种情况然后暴力枚举判断是否为正方形. 组合数的意义表示在 2、3、4 三个点中有序选出两个点和 1 相邻.
- 我写了一百多行的代码, 看了看别人简洁的代码, 发现可以直接用STL的
next_permutation
来生成下一个排列, 把排列看做从左上角开始的顺时针 (或逆时针) 四个点的编号, 就可以少打很多暴力枚举.
- 尤其是在点数更多时这个办法更有用.
- 然后就去看题解, 发现可以直接用三个点来判断, 用向量法推出第四个点的坐标, 判定是否符合.
- 计算几何的一些常用函数还是打成函数比较好, 可以降低代码复杂度.
代码
https://code.youkuaiyun.com/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 (i≠j) , 使得 gcd(ai,aj) 取到最大值. - 数组元素个数 n≤50000
分析
- 因为数组是给出的, 有不确定性, 所以放弃了找规律.
- 暴力拆解因数, 用 hash 表来存储这个因数有没有出现过. 如果这个因数出现过就更新答案, 否则标记为已出现.
- 枚举因数肯定是
[1,n√]
之内的, 同时如果
i
是
x 的因数, xi 也是 x 的因数, 如果xi 出现过, 那么就可以不再继续枚举了. 因为随着 i 的增大,xi 会减小, 不会比现在答案更优. - O(nn√)
- 还可以加一个优化, 就是把 a 数组 sort 后从大往小来判定. 这样提前搜到答案的几率或许更大一些.
代码
https://code.youkuaiyun.com/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
个积木, 剩余
i−j 个积木, 为了不重复, 把它们全部放在新的一行. 假设 s[i] 表示 h[i]−h[i−1] , 也就是差分. 规定 h[0]=1,h[i−j+1]=m . 则 ∑i−j+1i=1si=m−1 , 求正整数解就可以限定 h 的取值在 [1,m] 之间. 正整数解的个数为 (i−j+1+m−1−1i−j+1−1)=(i−j+m−1i−j) . - 看题解还发现一个细节, 就是先拿出第一个积木来. 这样可以保证该积木不会包含在那 j 个积木中, 只能在新的一行中. 保证不会出现重复.
- 假设已经排好
j
个积木, 剩余