Codeforces Round #291 (Div. 2) B. Han Solo and Lazer Gun

B. Han Solo and Lazer Gun
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

There are n Imperial stormtroopers on the field. The battle field is a plane with Cartesian coordinate system. Each stormtrooper is associated with his coordinates (x, y) on this plane.

Han Solo has the newest duplex lazer gun to fight these stormtroopers. It is situated at the point (x0, y0). In one shot it can can destroy all the stormtroopers, situated on some line that crosses point (x0, y0).

Your task is to determine what minimum number of shots Han Solo needs to defeat all the stormtroopers.

The gun is the newest invention, it shoots very quickly and even after a very large number of shots the stormtroopers don't have enough time to realize what's happening and change their location.

Input

The first line contains three integers nx0 и y0 (1 ≤ n ≤ 1000 - 104 ≤ x0, y0 ≤ 104) — the number of stormtroopers on the battle field and the coordinates of your gun.

Next n lines contain two integers each xiyi ( - 104 ≤ xi, yi ≤ 104) — the coordinates of the stormtroopers on the battlefield. It is guaranteed that no stormtrooper stands at the same point with the gun. Multiple stormtroopers can stand at the same point.

Output

Print a single integer — the minimum number of shots Han Solo needs to destroy all the stormtroopers.

Sample test(s)
input
4 0 0
1 1
2 2
2 0
-1 -1
output
2
input
2 1 2
1 1
1 0
output
1
Note

Explanation to the first and second samples from the statement, respectively:


题目大意:

 

在一个平面直角坐标系上坐落有n个帝国突击队的士兵,现在给你一把最新型的双头激光枪,你的目标是用最少的射击次数kill所有的士兵,已知士兵的坐标和激光枪的坐标(固定不变),且双头激光枪每射击一次会kill掉所在直线上的所有士兵。要你求最少的射击次数。

 

大致思路:

 

一开始看到这道题想到的是枚举激光枪的所有方向,只要那个方向有兵,射击次数就加一,后来发现自己想的太简单了,实际上激光枪的射击方向有无数个,这样很明显行不通,如果真这样妥妥超时。

 

后来由于看到平面直角坐标系,就想到了可以通过计算每个士兵和激光枪所在直线的斜率和截距,如果有不同的就加一,事实证明这样是有效的。这里有一个细节,因为斜率和截距不一定是整数,所以得用double来储存,既然如此,那在比较是否有相同的时候得用fabs(a-b)<1e-xx为常数)是否成立来判断是否相等,否则会WA

 

比赛结束后看了官方题解和别人的代码,发现方法比较类似,但做法还是有些许差别,因为这里涉及到浮点数的比较问题,所以特定写写,帮助自己理一下,总结一下。

 

我发现别人是将所有的斜率计算后储存在数组里,再用sort排一下,然后遍历一遍,遇到相同的就跳过,不同就加一。本来我感觉方法一样,可没想到问题出来了……我发现用这种方法他的浮点数比较可以直接用“==”来判断(当然也可以用fabs(a-b)<1e-xx为常数)),而换成我那种却不行。

 

还有一点,我觉得其实我没必要比较截距是否相同,直接比较斜率就可以,然后改了一下提交发现WA了…我前前后后提交了十几发,发现了几点——

 

1.我那种方法也可以直接比较斜率,但必须fabs(a-b)<1e-9才行,不能直接用“==”比较。

2.第二种方法可以直接用“==”比较。而如果要用fabs()比较的话也必须<1e-9,你<1e-8都不行。

3.我那种方法如果斜率截距都比较的话精度控制可以<1e-5就可以了。如果用“==”同样也是WA的。

 

后来经过叉姐指导,发现误差数值分析也是很重要的,就像这道题,他的数据范围是-10^4~10^4,那fabs就要精确到2.5e-9之后,是这样算的:斜率之差可以简单表示为a/b - c/d,也就是(ad-bc)/bd,而bd最大为4e-82*10^4 * 2*10^4),(ad-bc)最小为1,这样斜率之差达到最小,我们必须保证最小斜率之差也可以比较无误,所以精度控制到1/4e-8之后,也就是2.5e-9之后…

然后我也尝试了下,的确用3e-9WA了,2e-9AC了。

Ps: 1/4e-8 == 2.5e-9

 

叉姐说其实这样也不是很对,要考虑到C++的精度还是什么的(我不是很懂),现在还不需要考虑这些……直接每次无脑设1e-10就可以了233333


我觉得可能两种方法的姿势都不太对,看到大牛好像使用gcd什么的来求解,不过不是很懂,以后再来说。

 

还有一点第3点为什么比较两项eps可以设置的不那么小呢,我想是因为用两项比较精度也提高了吧。至于为什么会出现两种方法比较精度不同我暂时也想不通,先占个坑,以后懂了再来填坑~~~~

 

罗里吧嗦那么多,下面放出代码:


//cf291_B.cpp 
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <map>
typedef long long ll;
using namespace std;
const int K = 10000;
const int maxm = 100000000;
bool s[maxm+10];
double kk[K], bb[K];
int main(void)
{
	int n, x0, y0, x, y, a, b;
	ll cnt = 0;
	scanf("%d %d %d", &n, &x0, &y0);
	int len = 0, fx = 1;
	while( n-- )
	{
		bool flag = false;
		scanf("%d %d", &x, &y);
		if( x==x0 )
		{
			if( fx==1 )
			{
				cnt++;
				fx = 0;				
			}
			continue;
		}
		double k = (y-y0)*1.0/(x-x0);
		kk[len++] = k;
		// double b = y - (y-y0)*1.0/(x-x0)*x;
		// for( int i=0; i<len; i++ )
		// {
		// 	if( fabs(kk[i]-k)<1e-5 && fabs(bb[i]-b)<1e-5 )
		// 	{
		// 		flag = true;
		// 		break;
		// 	}
		// }
		// if( flag==false )
		// {
		// 	kk[len] = k;
		// 	bb[len] = b;
		// 	len++;
		// 	cnt++;
		// }
	}
	sort(kk, kk+len);
	for( int i=0; i<len; i++ )
	{
		while( fabs(kk[i+1]-kk[i])<2e-9 && i<len )
			i++;
		cnt++;
	}
	printf("%d\n", cnt);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值