N諾轟炸,poj 1118 Lining Up【共線問題/枚舉】

“我該怎麼辦?”飛行員klux向你求助。 事實上,klux面對的是一個很簡單的問題,但是他實在太菜了。 klux要想轟炸某個區域內的一些地方,它們是位於平面上的一些點,但是(顯然地)klux遇到了抵抗,所以klux只能飛一次,而且由於飛機比較破,一點起飛就只能沿直線飛行,無法轉彎。現在他想一次轟炸最多的地方。 不限定起飛地點


首先我們直觀的方法是找出所給點集的所有可能的直線,然後對於每條直線,看看有多少個其他的點在該直線上. 最後更新最大值即可.

首先需要知道如何判斷三點共線:

對已有兩個點p1,p2,我們另找一個點p3,如果三點共線,那麼向量p1p2,p1p3平行即可(因爲有公共點p1,所以只要平行三個點就共線),那麼第一個向量爲(x2x1,y2y1)(x2-x1,y2-y1),第二個向量爲(x3x1,y3y1)(x3-x1,y3-y1)那麼平行即(x2x1)(y3y1)==(y2y1)(x3x1)(x2-x1)(y3-y1)==(y2-y1)(x3-x1),也可以通過斜率來理解:

判斷向量(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;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章