HDU 1542 Atlantis (离散化+扫描线)

刚开始学习扫描线,由于网上资料介绍比较抽象(实际是本人智力水平不是很高),整整看了一个上午才对扫描线有点眉目,然后又花了大半个下午才弄出这道基础题,真是忏愧。另外刚开始学习扫描线,我认为代码写的是不太好的,但是将就着,等以后水平高些回来改。会附上比较详细的注释,毕竟是新手。

/*我这里采用了离散化(对结点增加了两个实际值端点)。
看过几个其他解法,如果不怕数据太强,可以用map<double,int>直接检索线段区间就不用离散化。即map<y坐标,位置>
另外还有先用二分查找先找到线段的区间的,感觉比较麻烦。不过想法都很强ORZ*/
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#define MAX 205
using namespace std;
struct node{
	double x,y1,y2; //一条垂直线段的坐标x,y1,y2
	int flag;		//代表这条线段是出边(先扫到的矩形一边)还是入边(后扫到的矩形另一边),1代表入边,-1代表出边。
	node(){}
	node(double a,double b,double c,int d){x=a,y1=b,y2=c,flag=d;}	//构造函数比较方便
}line[MAX];
bool cmp(node a,node b){		//后面要对线段根据x排序
	return a.x<b.x;
}
struct Node{			//线段树结点定义,L,R左右端点,flag代表该区间重叠次数,叶子结点都代表一个线段
	int L,R,flag;
	double dd,uu,cnt;		//dd,uu看后面赋值即知,cnt代表该结点覆盖线段的长度
	void fun(int value){flag+=value;if(flag==0)cnt=0;else cnt=uu-dd;} //重叠次数根据出入边的判断要进行操作
}K[MAX<<2];
double Y[MAX];
void build(int l,int r,int rt){				//建树
	K[rt].L=l,K[rt].R=r;
	K[rt].dd=Y[l],K[rt].uu=Y[r];		//离散化
	K[rt].flag=0,K[rt].cnt=0;
	int m=l+r>>1;
	if(l!=r-1){
		build(l,m,rt<<1);		//两个端点才构成一个线段,所以叶子结点的L=R-1
		build(m,r,rt<<1|1);
	}
	return ;
}
void update(node line,int rt){		//更新
	if(K[rt].dd==line.y1&&K[rt].uu==line.y2){	//使用了延迟,其实也可以直接到叶子结点再全部更新回来
		K[rt].fun(line.flag);		
		if(K[rt].L!=K[rt].R-1&&K[rt].flag==0){//其实就是说某父节点在获取一个出边时的操作,不能像叶子结点一样CNT=0,而要更新(这里使用了一个性质,一入边必有一相同区间的出边)
			K[rt].cnt=K[rt<<1].cnt+K[rt<<1|1].cnt;
		}
		return;
	}				
	if(line.y1>=K[rt<<1|1].dd)update(line,rt<<1|1);			//分区更新
	else if(line.y2<=K[rt<<1].uu)update(line,rt<<1);	
	else			
	{
		node temp=line;
		temp.y1=K[rt<<1|1].dd;
		update(temp,rt<<1|1);
		temp=line;
		temp.y2=K[rt<<1].uu;
		update(temp,rt<<1);
	}
	if(K[rt].flag==0)								//如果父节点有覆盖则其cnt肯定最大了,不需要更新
		K[rt].cnt=K[rt<<1].cnt+K[rt<<1|1].cnt;
}
int main(){
	int n;
	double x1,y1,x2,y2;
	int p=1;
	while(~scanf("%d",&n)&&n){
		for(int i=1;i<=n;i++){
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			line[2*i-1]=node(x1,y1,y2,1);
			Y[2*i-1]=y1;
			line[2*i]=node(x2,y1,y2,-1);
			Y[2*i]=y2;
		}
		sort(Y+1,Y+1+2*n);
		sort(line+1,line+1+2*n,cmp);
		build(1,2*n,1);
		double res=0;
		for(int i=1;i<=2*n;i++){
		
			if(i!=1)res+=K[1].cnt*(line[i].x-line[i-1].x);
			update(line[i],1);
		}
		printf("Test case #%d\n",p++);
		printf("Total explored area: %.2f\n\n",res);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值