1018 - 计算几何之凸包 - 水平可见直线(BZOJ 1007)

传送门

 

分析

遇到新的题,要思考着往学过的模型上套

这道题要求的是水平可见直线,其实稍微转化一下,就会发现最后能够被看见的直线恰好形成了一个开口向上的半凸包的样子

我们画出一个半凸包,可以发现每条边的斜率是在从左往右依次增加,而其交点横坐标的大小也在递增

可以利用这个性质,对这道题进行求解

将直线按斜率从小到大的顺序排序,依次加入栈中,当当前直线与栈顶直线的交点位于栈中前两条直线交点的左边时,说明栈顶直线会被新加入的直线所挡住,弹出栈顶元素

这样一直做下去,最后留在栈中的直线就是可见的

 

最后。别忘了判重,思考极端数据:

 

有毒吧……建议大家到洛谷上去交这道题,BZOJ的数据水啊

代码

#include<bits/stdc++.h>
#define N 500009
#define in read()
#define eps 1e-8
using namespace std;
inline int read(){
    char ch;int f=1,res=0;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    while(ch>='0'&&ch<='9'){
        res=(res<<3)+(res<<1)+ch-'0';
        ch=getchar();
    }
    return f==1?res:-res;
}
struct point {
    double x,y;
};
struct Line{
    double k,b;
    int id;
}line[N];
int n;
double a,b;
bool cmp(const Line &a,const Line &b){
    if(fabs(a.k-b.k)<eps) return a.b<b.b;
    return a.k<b.k;
}

bool cmp2(const Line &a,const Line &b)
{
    return a.id<b.id;
}
Line s[N];
double ask(Line a,Line b){
    return (b.b-a.b)/(a.k-b.k);
    
}
int main(){
    n=in;
    int i,j;
    for(i=1;i<=n;++i)	
    {
        scanf("%lf%lf",&a,&b);
        line[i].k=a;line[i].b=b;
        line[i].id=i;
    }	
    if(n==1) {printf("1 ");return 0;}
    if(n==2) {
        int num=(line[2].b-line[1].b>eps)?2:1;
        if(fabs(line[1].k-line[2].k)<eps) printf("%d ",num);
        else printf("1 2 ");
        return 0;
    }
    sort(line+1,line+n+1,cmp);
    double inter;
    int qn=0;
    s[++qn]=line[1];
    for(i=2;i<=n;++i){
        if(fabs(line[i].k-s[qn].k)<eps) qn--;
        while(qn>1&&ask(line[i],s[qn] )<=ask(s[qn],s[qn-1])) qn--;
        s[++qn]=line[i];
    }
    sort(s+1,s+qn+1,cmp2);
    for(i=1;i<=qn;++i) printf("%d ",s[i].id);
    return 0;
}

 

最后一点WA的代码

#include<bits/stdc++.h>
#define N 500009
#define in read()
#define eps 1e-8
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
struct point {
	double x,y;
};
struct Line{
	double k,b;
	int id;
}line[N];
int n;
double a,b;
bool cmp(const Line &a,const Line &b){
	if(fabs(a.k-b.k)<eps) return a.b<b.b;
	return a.k<b.k;
}

bool cmp2(const Line &a,const Line &b)
{
	return a.id<b.id;
}
Line s[N];
double ask(Line a,Line b){
	return (b.b-a.b)/(a.k-b.k);
}
int main(){
	n=in;
	int i,j;
	for(i=1;i<=n;++i)	
	{
		scanf("%lf%lf",&a,&b);
		line[i].k=a;line[i].b=b;
		line[i].id=i;
	}	
	if(n==1) {printf("1 ");return 0;}
	if(n==2) {
		int num=(line[2].b-line[1].b>eps)?2:1;
		if(fabs(line[1].k-line[2].k)<eps) printf("%d ",num);
		else printf("1 2 ");
		return 0;
	}
	sort(line+1,line+n+1,cmp);
	double inter;
	int qn=0;
	s[++qn]=line[1];s[++qn]=line[2];
	for(i=3;i<=n;++i){
		if(fabs(line[i].k-s[qn].k)<eps) qn--;//如果第一条边不合法,则不会被弹掉,改为while即可
		while(qn>1&&ask(line[i],s[qn] )<=ask(s[qn],s[qn-1])) qn--;
		s[++qn]=line[i];
	}
	sort(s+1,s+qn+1,cmp2);
	for(i=1;i<=qn;++i) printf("%d ",s[i].id);
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值