计蒜客 商汤科技的行人检测(随机化+计算几何)
题目链接
简单版本
直接统计答案,数目到达一半即可。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) int n; int a, b, c, d; pair <int, int> ans; map <pair <int, int >, int > mp; int main(){ scanf("%d", &n); rep(i, 1, n){ scanf("%d%d%d%d", &a, &b, &c, &d); ++mp[{c - a, d - b}]; if (mp[{c - a, d - b}] >= (n + 1) / 2) ans = {c - a, d - b}; } printf("%d %d\n", ans.first, ans.second); return 0; }
中等版本
题面和简单版本不一样。
我们可以通过两组变化解出一组行人的移动参数
具体的解法是
scale可以通过变换后两点之间距离的倍数关系求出
旋转坐标前两个点形成的直线向量A,和旋转坐标后的两个点形成的直线向量B
那么θ就是A,B的夹角,然后用一下公式$cosθ=\frac{A*B}{|A|*|B|}$, 就能算出角度了。
那么dx, dy就很好求了。
我们两两枚举所有的变化,求出参数,然后验证一下在n组变化中吻合次数是否到达一半。
(注意eps要开1e-4,我之前开了1e-8一直WA)
时间复杂度$O(n^3)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e5 + 10; const double eps = 1e-4; struct node{ double x, y; void scan() { scanf("%lf%lf", &x, &y); } void print(){ printf("%.12f %.12f\n", x, y); } friend node operator - (const node &a, const node &b){ return node{a.x - b.x, a.y - b.y}; } friend node operator + (const node &a, const node &b){ return node{a.x + b.x, a.y + b.y}; } friend bool operator == (const node &a, const node &b){ return fabs(a.x - b.x) < eps && fabs(a.y - b.y) < eps; } } a[N], b[N]; struct Node{ double cita, scale, dx, dy; void print(){ printf("%.12f\n%.12f\n%.12f %.12f\n", cita, scale, dx, dy); } } ret; int n; double dis(const node &a, const node &b){ return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } double angle(const node &a){ return atan2(a.y, a.x); } node calc(double ang, node a){ double x = a.x, y = a.y; a.x = x * cos(ang) - y * sin(ang); a.y = x * sin(ang) + y * cos(ang); return a; } bool judge(Node cnt){ int ret = 0; node now; rep(i, 1, n){ now = calc(cnt.cita, a[i]); now.x *= cnt.scale; now.y *= cnt.scale; now.x += cnt.dx; now.y += cnt.dy; if (now == b[i]) continue; ++ret; if (ret * 2 > n) return false; } return true; } Node solve(int x, int y){ Node ret = {0, 0, 0, 0}; ret.cita = angle(b[y] - b[x]) - angle(a[y] - a[x]); ret.scale = dis(b[x], b[y]) / dis(a[x], a[y]); node cnt = calc(ret.cita, a[x]); ret.dx = b[x].x - ret.scale * cnt.x; ret.dy = b[x].y - ret.scale * cnt.y; return ret; } int main(){ scanf("%d", &n); rep(i, 1, n){ a[i].scan(); b[i].scan(); } if (n == 1){ puts("0.000000000000"); puts("1.000000000000"); node c = b[1] - a[1]; c.print(); return 0; } bool flag = false; rep(i, 1, n - 1){ rep(j, i + 1, n){ ret = solve(i, j); if (judge(ret)){ flag = true; break; } } if (flag) break; } ret.print(); return 0; }
困难版本
因为错误的参数不超过一半,那么我们选择一个点,他的参数错误的概率不超过0.5
所以我们选择两个点,这两个点至少有一个点的参数错误的概率不超过0.75
我们随机枚举100次,那么枚举得到的的100组参数都错的概率为$0.75^{100}$,几乎为0
但是中等的代码交到困难版本这边是TLE的,为什么呢。
因为中等的代码是有序枚举的,如果前面很多的点参数都是错的,那么就枚举不到正确答案,于是就TLE了。
时间复杂度$O(100n)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 1e5 + 10; const double eps = 1e-4; struct node{ double x, y; void scan() { scanf("%lf%lf", &x, &y); } void print(){ printf("%.12f %.12f\n", x, y); } friend node operator - (const node &a, const node &b){ return node{a.x - b.x, a.y - b.y}; } friend node operator + (const node &a, const node &b){ return node{a.x + b.x, a.y + b.y}; } friend bool operator == (const node &a, const node &b){ return fabs(a.x - b.x) < eps && fabs(a.y - b.y) < eps; } } a[N], b[N]; struct Node{ double cita, scale, dx, dy; void print(){ printf("%.12f\n%.12f\n%.12f %.12f\n", cita, scale, dx, dy); } } ret; int n; double dis(const node &a, const node &b){ return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } double angle(const node &a){ return atan2(a.y, a.x); } node calc(double ang, node a){ double x = a.x, y = a.y; a.x = x * cos(ang) - y * sin(ang); a.y = x * sin(ang) + y * cos(ang); return a; } bool judge(Node cnt){ int ret = 0; node now; rep(i, 1, n){ now = calc(cnt.cita, a[i]); now.x *= cnt.scale; now.y *= cnt.scale; now.x += cnt.dx; now.y += cnt.dy; if (now == b[i]) continue; ++ret; if (ret * 2 > n) return false; } return true; } Node solve(int x, int y){ Node ret = {0, 0, 0, 0}; ret.cita = angle(b[y] - b[x]) - angle(a[y] - a[x]); ret.scale = dis(b[x], b[y]) / dis(a[x], a[y]); node cnt = calc(ret.cita, a[x]); ret.dx = b[x].x - ret.scale * cnt.x; ret.dy = b[x].y - ret.scale * cnt.y; return ret; } int main(){ scanf("%d", &n); rep(i, 1, n){ a[i].scan(); b[i].scan(); } if (n == 1){ puts("0.000000000000"); puts("1.000000000000"); node c = b[1] - a[1]; c.print(); return 0; } rep(Case, 1, 100){ int x = rand() % n + 1; int y = rand() % n + 1; if (x == y) continue; ret = solve(x, y); if (judge(ret)) break; } ret.print(); return 0; }