补题向 | Large Triangle(计算几何+二分)

Large Triangle

给出n个点的坐标,求由其中三个点组成的三角形里面有没有面积等于s的

在网上找题解,可以暴力+hash

不够更多的做法是用旋转坐标系+二分

做这道题时先看了一道叫做圈地的题,是求点组成的面积最小的三角形,看了很久才稍微懂了一点,先将点按照x坐标排序,然后每两点构成的线段按照斜率排序,在处理某一条线段ab时,由于各个点按照x坐标排了序,理论上按照顺序在a、b两边的点就是离线段ab最近的点,那么以ab为y轴时,高就是ab,而底就是点与y轴的距离,那么将离y轴最近的点作为三角形的第三个点,这个三角形就是以ab为高时所能求出的最小面积的三角形,而在新坐标系中,只有a、b相对位置会发生变化,因此,每次处理完一条线段,就交换两点顺序(说实话,这点是我最不能想通的地方,只好由结论反推,具体看代码注释),只要枚举所有线段,就能求出最小面积的三角形了

在圈地基础上,分别在a、b两端二分查找,找出使得面积刚好为s的点,枚举所有线段

#include<stdio.h>
#include<vector>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<fstream>
#include<math.h>
#include<stack>
#include<bitset>
#include<utility>
using namespace std;
typedef long long ll;
const double eps=0.0000000000001;
const int mod=1000000007;
int n;
ll s;
bool f;
struct P{
	ll x;
	ll y;
};
P p[2005];
struct D{
	int i;
	int j;
	double w;
};
D dt[4000025];
int id[2005];
int pos[2005];
ll cross(P b,P a,P c){
	return fabs((ll)(b.x-a.x)*(c.y-a.y)-(ll)(b.y-a.y)*(c.x-a.x));
}
bool cmp(D a,D b){
	return a.w<b.w;
}
bool cmpp(P a,P b){
	if(a.x==b.x)return a.y<b.y;
	return a.x<b.x;
} 
void print(P a,P b,P c){
	printf("Yes\n");
	printf("%I64d %I64d\n",a.x,a.y);
	printf("%I64d %I64d\n",b.x,b.y);
	printf("%I64d %I64d\n",c.x,c.y);
}

int main(){
	scanf("%d%I64d",&n,&s);
	ll x,y;
	for(int i=0;i<n;i++){
		scanf("%I64d%I64d",&x,&y);
		p[i].x=x;
		p[i].y=y;
	}
	sort(p,p+n,cmpp);//点以x坐标为第一关键字,y为第二关键字排序 

	int k=0;
	for(int i=0;i<n-1;i++){
		for(int j=i+1;j<n;j++){
			dt[k].i=i;
			dt[k].j=j;
			dt[k].w=(double)(p[j].y-p[i].y)/(p[j].x-p[i].x);
			//a/b为double时,若b=0,a/b=inf 
			k++;
		}
	}
	//dt[k]中存放线段两个端点坐标和斜率 
	sort(dt,dt+k,cmp);//所有线段按斜率排序 
	for(int i=0;i<n;i++){
		pos[i]=id[i]=i;
	}
	//pos中存放各个点位置,id中存放每个位置对应点的id
	//处理完一条线段之后点的位置会更新,pos中的点在新的坐标系中顺序和在x轴上顺序相同 
	for(int i=0;i<k;i++){
		int x=dt[i].i;
		int y=dt[i].j;
		if(pos[x]>pos[y])swap(x,y);
		int l=0,r=pos[x]-1;
		while(l<=r){
			int mid=(l+r)/2;
			ll w=cross(p[id[mid]],p[x],p[y]);
			if(w==s*2){
				print(p[id[mid]],p[x],p[y]);
				return 0;
			}
			else if(w<s*2){
				r=mid-1;//在线段xy左边的点,由于以xy为y轴,越大离xy越近,面积越小
			}
			else l=mid+1;
		}
		 
		l=pos[y]+1,r=n-1;
		while(l<=r){
			int mid=(l+r)/2;
			ll w=cross(p[id[mid]],p[x],p[y]);
			if(w==s*2){
				print(p[id[mid]],p[x],p[y]);
				return 0;
			}
			else if(w<s*2){
				l=mid+1;
			}
			else r=mid-1;
		}
		//以线段xy为y轴分别在左右两边二分查找 
		swap(pos[x],pos[y]);
		swap(id[pos[x]],id[pos[y]]);
		/*在以x(或y)为其中一个端点,且斜率更高的直线xz为y轴为新坐标系的图中
		若之前点z与直线xy距离(x轴方向上)是d,将x和y距离看为0,则点的顺序为:x y z
		而在新坐标系中,x与z距离为0,y与xz距离为d,点顺序变为:y x z 
		所以每次旋转坐标系时,只有x和y相对位置会发生变化
		所以每处理完一条直线则交换两点顺序 
		*/  
	}
	printf("No\n");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/bekote/article/details/81908131
今日推荐