计蒜客 商汤科技的行人检测(随机化+计算几何)

题目链接

简单

中等

困难

简单版本

直接统计答案,数目到达一半即可。

#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;
}

 

转载于:https://www.cnblogs.com/cxhscst2/p/7532975.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值