N诺轰炸,poj 1118 Lining Up【共线问题/枚举】

“我该怎么办?”飞行员klux向你求助。 事实上,klux面对的是一个很简单的问题,但是他实在太菜了。 klux要想轰炸某个区域内的一些地方,它们是位于平面上的一些点,但是(显然地)klux遇到了抵抗,所以klux只能飞一次,而且由于飞机比较破,一点起飞就只能沿直线飞行,无法转弯。现在他想一次轰炸最多的地方。 不限定起飞地点


首先我们直观的方法是找出所给点集的所有可能的直线,然后对于每条直线,看看有多少个其他的点在该直线上. 最后更新最大值即可.

首先需要知道如何判断三点共线:

对已有两个点p1,p2,我们另找一个点p3,如果三点共线,那么向量p1p2,p1p3平行即可(因为有公共点p1,所以只要平行三个点就共线),那么第一个向量为 ( x 2 − x 1 , y 2 − y 1 ) (x2-x1,y2-y1) (x2x1,y2y1),第二个向量为 ( x 3 − x 1 , y 3 − y 1 ) (x3-x1,y3-y1) (x3x1,y3y1)那么平行即 ( x 2 − x 1 ) ( y 3 − y 1 ) = = ( y 2 − y 1 ) ( x 3 − x 1 ) (x2-x1)(y3-y1)==(y2-y1)(x3-x1) (x2x1)(y3y1)==(y2y1)(x3x1),也可以通过斜率来理解:

判断向量(p1–>p2)和向量(p1–>p3)的斜率是否相等。即 (y2-y1)/(x2-x1) == (y3-y1)/(x3-x1).
这个除式判断可以改写成乘式判断:(y3−y1)(x2−x1)−(y2−y1)(x3−x1)=0
(改写的原因是除法有分母为0或精度等问题,总之乘法更方便!)
注意,如果坐标本身是浮点型,尽量不要用“==”进行比较,因为在计算机中小数会有一定的误差,这时应该取一定的误差,例如
|(y3−y1)(x2−x1)−(y2−y1)(x3−x1)|<=1e−6

程序1实现找用到了定序的技巧,保证不会丢失最优解. 比如某条直线上有5个点且是最多的.这5个点分别编号为1,4,5,7,8.那么我们用下面第2种方式来实现代码:

//192K	532MS	
#define inf 0x3f3f3f3f
#define vec vector<int>
#define P pair<int,int>
#define MAX 705
#define ll long long

int n;
P a[MAX];
int main() {
	while (scanf("%d", &n) != EOF && n) {
		int res = 0, cnt = 0;
		for (int i = 0; i < n; i++)
			scanf("%d %d", &a[i].first, &a[i].second);
		for (int i = 0; i < n; i++) {
			for (int j = i + 1; j < n; j++) {
				cnt = 2;
				for (int k = j + 1; k < n; k++) {
					if((a[j].first-a[i].first)*(a[k].second-a[i].second)==(a[j].second-a[i].second)*(a[k].first-a[i].first))
						cnt++;
					if (cnt > res)res = cnt;
				}
			}
		}
		cout << res << endl;
	}
}

下面是通过排序来降低一点复杂度的方法二.

我们枚举每一个点,把该点作为固定点,然后我们计算剩下的所有点与该点构成直线的斜率(注意处理斜率不存在的情况),把这些斜率保存在数组S中,然后对S数组排序,那么斜率相同的值就会变成连续存放.那些斜率相同的值就代表的共线的点,这样就可算出最多有多少点共线.(程序实现同样用了定序的技巧)

//204K	157MS
#define inf 0x3f3f3f3f
#define vec vector<int>
#define P pair<int,int>
#define MAX 705
#define ll long long

int n;
double lines[MAX*MAX];
P a[MAX];

int main() {
	while (scanf("%d", &n) != EOF && n) {
		int res = 0, cnt = 0, j = 0;
		for (int i = 0; i < n; i++)
			scanf("%d %d", &a[i].first, &a[i].second);
		for (int i = 0; i < n; i++) {
			int num = 0, cnt = 0, notexi = 1;
			for (int j = i + 1; j < n; j++) {//以一个点为基准,计算和其他点的斜率
				if (a[j].first == a[i].first)notexi++;//斜率不存在的情况
				else {
					//计算斜率
					lines[num++] = (1.0*a[j].second - a[i].second) / (a[j].first - a[i].first);
				}
			}
			//排序处理
			sort(lines, lines + num);
			for (int i = 0; i < num; i = j) {
				cnt = 2;
				for (j = i + 1; abs(lines[j] - lines[i]) <= 1e-6; j++)
					cnt++;
				if (cnt > res)res = cnt;
			}
			if (notexi > res)res = cnt;//斜率不存在的这些点也共线
		}
		cout << res << endl;
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值