自由堆叠的屋顶
总提交 : 109 测试通过 : 39
比赛描述
sed 同学最近突发奇想,认为伟大的建筑物的屋顶应该是“自由堆叠”出来的,他的设计方案是:将各种颜色的长方形建筑板材堆叠在一起,并保证各个板材长边、宽边均相互平行或在一条直线上,板材之间的重叠部分用连接装置固定在一起。
你的任务是计算这个“自由堆叠的屋顶”所覆盖的面积。sed 将会在屋顶平面上建立一个二维坐标系,提供给你每个长方形建筑板材左上角、右下角的坐标。为简化计算,这里忽略板材的厚度,假设它们都在同一个平面上。
输入
输入数据包含多组测试案例。
每组测试案例由N(0≤N≤100)开头,后续N行每行包含4个实数x1;y1;x2;y2 (0 <= x1 < x2 <= 100000建筑单位;0 <= y1 < y2 <= 100000建筑单位)。
(x1; y1) 、 (x2;y2)分别是长方形建筑板材左上角、右下角的坐标。
单独一个行输入0表示输入结束,无需处理。
输出
对于每个测试用例,输出以下信息: 第1行形如“Build #k”,这里k是测试用例序号(以1开始),第二行形如“Total area: a”,a是总覆盖面积,保留两位小数。
样例输入
2
10 10 20 20
15 15 25 25.5
1
10 10 20 20
0
样例输出
Build #1
Total explored area: 180.00
Build #2
Total explored area: 100.00
题目来源
南京邮电大学计算机学院首届ACM程序设计大赛(2009)
我的答案(Java):(WrongAnswerAtTest1 不知道问题出在哪儿)
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main{
public final int MAXN=100;
public int N;// 板块个数
public float[] position=new float[MAXN*4];// 板块坐标
public float[] X;// 保存线段的横坐标
public Line[] lines;// 保存线段
public Node[] segTree=new Node[MAXN*8];
public void input(Scanner sc) throws Exception{
try {
// 根据输入的n来创建数组
// int posNum = 4 * N;
// position = new float[posNum];
for (int i = 0; i < N; i++) {
float x1 = sc.nextFloat();
float y1 = sc.nextFloat();
float x2 = sc.nextFloat();
float y2 = sc.nextFloat();
if (!checkXY(x1, y1, x2, y2))
throw new Exception("坐标不合法");
position[i * 4 + 0] = x1;
position[i * 4 + 1] = y1;
position[i * 4 + 2] = x2;
position[i * 4 + 3] = y2;
}
} catch (Exception e) {
throw e;
}
}
/**
* 检查坐标是否合法
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
public boolean checkXY(float x1,float y1,float x2,float y2 ){
if (check(x1) && check(x2) && check(y1) && check(y2) && x1 < x2
&& y1 < y2)
return true;
return false;
}
private boolean check(float x){
if(x<0||x>100000)
return false;
return true;
}
/**
* 将矩形的横边转化为线段,按照y值排序;同时将横坐标排序去重
* @return
*/
public void transfer(){
lines=new Line[N*2];
X=new float[N*2];
for(int i=0;i<N;i++){
float x1=position[i*4+0];
float y1=position[i*4+1];
float x2=position[i*4+2];
float y2=position[i*4+3];
Line line1=new Line(x1, x2, y1);
line1.seq=1;// 下边
Line line2=new Line(x1, x2, y2);
line2.seq=-1;// 上边
lines[i*2]=line1;
lines[i*2+1]=line2;
X[i*2]=x1;
X[i*2+1]=x2;
}
}
/**
* 线段排序
* @param lines
*/
public void sort(Line[] lines,int fromIndex,int toIndex){
Arrays.sort(lines,fromIndex,toIndex, new Comparator<Line>() {
@Override
public int compare(Line o1, Line o2) {
float f=o1.y-o2.y;
if(f>0)
return 1;
else if(f<0)
return -1;
else
return 0;
}
});
}
public void sort(float[] X,int fromIndex,int toIndex){
Arrays.sort(X,fromIndex,toIndex);
}
public int search(float[] X,int fromIndex,int toIndex,float x){
return Arrays.binarySearch(X,fromIndex,toIndex, x);
}
/**
* 构建线段树
* @param nodeIdx
* @param leftIdx
* @param rightIdx
*/
public void build(int nodeIdx,int leftIdx,int rightIdx){
if(segTree[nodeIdx]==null)
segTree[nodeIdx]=new Node();
segTree[nodeIdx].left=leftIdx;
segTree[nodeIdx].right=rightIdx;
segTree[nodeIdx].cover=0;// 默认值为0
segTree[nodeIdx].coverLength=0;
if(leftIdx+1==rightIdx){// 叶子结点
return;
}
int mid=segTree[nodeIdx].mid();
build(nodeIdx*2+1, leftIdx, mid);// 构造左子树
build(nodeIdx*2+2,mid,rightIdx);// 构造右子树
}
/**
* 更新线段树
* @param nodeIdx
* @param a 扫描到的线段的左边界
* @param b 扫描到的线段的右边界
* @param cover
*/
public void update(int nodeIdx,int a,int b,int cover){
Node node=segTree[nodeIdx];
if(node.left==a&&node.right==b){// 重合
node.cover+=cover;
getLen(nodeIdx);
return;
}
int mid=node.mid();
if (b <= mid) {// 更新左子树
update(nodeIdx << 1 | 1, a, b, cover);
} else if (a >= mid) {// 更新右子树
update(nodeIdx << 1 | 2, a, b, cover);
} else {// 左右子树都要更新
update(nodeIdx << 1 | 1, a, mid, cover);
update(nodeIdx << 1 | 2, mid, b, cover);
}
getLen(nodeIdx);
}
/**
* 获取某个区间被覆盖的长度
* @param nodeIdx
*/
public void getLen(int nodeIdx){
Node node=segTree[nodeIdx];
if(node.cover>0){// 被覆盖
node.coverLength= X[node.right]-X[node.left];
}else if(node.right==node.left+1){// 叶子结点,未被覆盖
node.coverLength=0;
}else{// 是线段,但没有完全覆盖,其覆盖长度需要根据孩子结点来计算
node.coverLength=segTree[nodeIdx*2+1].coverLength+segTree[nodeIdx*2+2].coverLength;
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
Main m=new Main();
int mCase=1;
float area=0;// 面积
try{
m.N=sc.nextInt();
while(m.N!=0){
if(m.N<0||m.N>100)
throw new Exception("N不合法");
m.input(sc);// 输入坐标
m.transfer();// 对输入数据进行处理
m.sort(m.lines,0,m.N*2);// 线段排序
m.sort(m.X,0,m.N*2);// 横坐标排序
int end=1;
for(int i=1;i<2*m.N;i++){// 横坐标去重
if(m.X[i]!=m.X[i-1]){
m.X[end++]=m.X[i];
}
}
end--;
m.build(0, 0, end);// 构造线段树
area=0;
for(int i=0;i<2*m.N-1;i++){// 扫描,最后一条线段不用扫描了
Line line=m.lines[i];
int a=m.search(m.X, 0, end+1,line.x1);// 在坐标数组中找到线段起点和终点对应的数组下标
int b=m.search(m.X, 0, end+1, line.x2);
m.update(0, a, b, line.seq);// 更新线段树
float dy=m.lines[i+1].y-line.y;// 当前线段和下一条线段y坐标的差值
area+=m.segTree[0].coverLength*dy;// 计算面积
}
DecimalFormat format=new DecimalFormat("0.00");
if(mCase==1)
System.out.println();
System.out.println("Build #"+mCase++);
System.out.println("Total explored area: "+format.format(area));// 输出
m.N=sc.nextInt();// 下一组输入
}
} catch (Exception e) {
// e.printStackTrace();
}finally{
sc.close();
}
}
/**
* 保存线段
* @author fangxuan
*
*/
public class Line{
public float x1,x2;
public float y;
public int seq;// 边序号,用来区别上下边
public Line(float x1, float x2, float y) {
this.x1 = x1;
this.x2 = x2;
this.y = y;
}
}
/**
* 线段树的结点
* @author fangxuan
*
*/
public class Node{
public int left;// 保存左边界在横坐标数组中的下标
public int right;// 保存右边界在横坐标数组中的下标
public int cover;// 0 线段未被覆盖;1 被覆盖
public float coverLength;// 该区间被覆盖的长度
public int mid(){// 求中间值
return (left+right)>>1;
}
}
}
用List集合代替数组,会Memory Limit Exceed
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
public class Main {
public static class Line {
public float x1, x2, y;
public int seq;
public Line(float x1, float x2, float y, int seq) {
this.x1 = x1;
this.x2 = x2;
this.y = y;
this.seq = seq;
}
}
public static class Segment {
public int left, right;
public int cover;
public float coverLength;
public Segment lChild, rChild;
public int mid() {
return (left + right) >> 1;
}
}
public static class SegTree {
public Segment root;
public List<Float> xList;
public void build(List<Float> xList) {
this.xList = xList;
int size = xList.size();
if (size == 0) {
root = null;
} else
root = build(0, size - 1);
}
private Segment build(int left, int right) {
Segment root = new Segment();
root.cover = 0;
root.coverLength = 0;
root.left = left;
root.right = right;
if (left + 1 == right) {
root.lChild = root.rChild = null;
} else {
int mid = root.mid();
root.lChild = build(left, mid);
root.rChild = build(mid, right);
}
return root;
}
public void update(Line line) {
if (root == null)
return;
int a = search(xList, line.x1);
int b = search(xList, line.x2);
update(root, a, b, line.seq);
}
private void update(Segment root, int a, int b, int seq) {
if (root.left == a && root.right == b) {
root.cover += seq;
getCoverLength(root);
return;
}
int mid = root.mid();
if (b <= mid) {
update(root.lChild, a, b, seq);
} else if (a >= mid) {
update(root.rChild, a, b, seq);
} else {
update(root.lChild, a, mid, seq);
update(root.rChild, mid, b, seq);
}
getCoverLength(root);
}
public void getCoverLength(Segment root) {
if (root.cover > 0) {
int r = root.right;
int l = root.left;
root.coverLength = xList.get(r) - xList.get(l);
} else if (root.left + 1 == root.right) {
root.coverLength = 0;
} else {
root.coverLength = root.lChild.coverLength
+ root.rChild.coverLength;
}
}
}
public static int search(List<Float> xList, float x) {
return Collections.binarySearch(xList, x);
}
public static void sortLine(List<Line> lines) {
Collections.sort(lines, new Comparator<Line>() {
@Override
public int compare(Line o1, Line o2) {
return (int) (o1.y - o2.y);
}
});
}
public static void sortX(List<Float> xList) {
Collections.sort(xList);
int size = xList.size();
for (int i = size - 1; i > 0; i--) {
if (xList.get(i) == xList.get(i - 1)) {
xList.remove(i);
}
}
}
public static void main(String[] args) {
int n;
int mCase = 0;
float area = 0;
int i = 0;
List<Float> xList = new ArrayList<Float>();
List<Line> lines = new ArrayList<Main.Line>();
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
while (n != 0) {
xList.clear();
lines.clear();
float x1;
float y1;
float x2;
float y2;
for (i = 0; i < n; i++) {
x1 = sc.nextFloat();
y1 = sc.nextFloat();
x2 = sc.nextFloat();
y2 = sc.nextFloat();
xList.add(x1);
xList.add(x2);
lines.add(new Line(x1, x2, y1, 1));
lines.add(new Line(x1, x2, y2, -1));
}
sortX(xList);
sortLine(lines);
SegTree segTree = new SegTree();
segTree.build(xList);
if (segTree.root != null) {
area = 0;
for (i = 0; i < lines.size() - 1; i++) {
segTree.update(lines.get(i));
area += segTree.root.coverLength
* (lines.get(i + 1).y - lines.get(i).y);
}
System.out.println("Build #" + (++mCase));
System.out.printf("Total explored area: %.2f\n", area);
}
n = sc.nextInt();
}
sc.close();
}
}