POJ_1151
这个题目是我第一次接触离散化的东西,看了黑书相关的部分后便按自己的理解设计了一个算法:
首先将x-y平面看成被矩形的四个边所在的直线切成了若干块,然后把每一块看成1个点,之后扫描一遍所有矩形,把这些矩形覆盖的点标记一下,最后再计算一下所有标记的点的面积即可。
这样做排序x坐标是O(nlogn)的复杂度,排序y坐标是O(nlogn),由于x和y轴最多被切2*n刀,所以最后得到的点的数量是n^2数量级的,这样标记矩形覆盖的点就是O(n^3)的复杂度,最后扫描的时候是O(n^2)的复杂度,提交之后TLE了。
之所以TLE是因为O(n^3)的复杂度过高了,我们必须降低这一部分的复杂度。对比最后扫描的复杂度,油然而生一个想法,能不能不标记矩形覆盖的点,而直接在最后扫描的时候就计算出覆盖的面积呢?如果这样可行的话就有希望降到O(n^2)的复杂度。
细想一下是可以的,我们在最后扫描的时候会先枚举x,然后y由小向大计算,如果矩形是对y1有序的话,我们就可以保证扫描一遍矩形就能计算出当前这个x范围上,有多少面积是被覆盖的。具体计算的时候用up和down表示一个连续的被覆盖部分的上端和下端,一开始置为-1,然后逐一扫描排序后的矩形,如果当前矩形的y1比up大,那么就说明不会和之前覆盖的部分有重叠了,于是就可以将前面覆盖部分的面积计算出来累加到结果中去了,如果当前矩形的y1不比up大,但y2比up大,就说明连续覆盖的面积又向上延伸了,更新一下up即可。
后来用O(n^2)的算法交的时候RE了一次,才发现我数组开小了,所以之前O(n^3)的算法为什么超时也就不清楚是因为数组开小了还是因为算法复杂度太高了,因为O(n^3)是10^6左右,理论上也应该可以AC的。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define INF 100010
#define MAXD 220
#define zero 1e-8
struct square
{
double x1, y1, x2, y2;
}s[MAXD];
int X, N;
double left[MAXD], right[MAXD], tx[MAXD];
int cmp1(const void *_p, const void *_q)
{
square *p = (square *)_p, *q = (square *)_q;
return p->y1 < q->y1 ? -1 : 1;
}
int cmp2(const void *_p, const void *_q)
{
double *p = (double *)_p, *q = (double *)_q;
return *p < *q ? -1 : 1;
}
double fabs(double x)
{
return x < 0 ? -x : x;
}
int dcmp(double x)
{
return fabs(x) < zero ? 0 : (x < 0 ? -1 : 1);
}
void init()
{
int i, j, k = 2;
tx[0] = -1, tx[1] = INF;
for(i = 0; i < X; i ++)
{
scanf("%lf%lf%lf%lf", &s[i].x1, &s[i].y1, &s[i].x2, &s[i].y2);
tx[k ++] = s[i].x1;
tx[k ++] = s[i].x2;
}
qsort(s, X, sizeof(s[0]), cmp1);
qsort(tx, k, sizeof(tx[0]), cmp2);
N = 0;
for(i = 1; i < k; i ++)
if(dcmp(tx[i] - tx[i - 1]) != 0)
{
left[N] = tx[i - 1], right[N] = tx[i];
++ N;
}
}
void solve()
{
int i, j, k;
double ans = 0, down, up;
for(i = 0; i < N; i ++)
{
down = up = -1;
for(j = 0; j < X; j ++)
if(dcmp(left[i] - s[j].x1) >= 0 && dcmp(right[i] - s[j].x2) <= 0)
{
if(dcmp(s[j].y1 - up) > 0)
{
ans += (right[i] - left[i]) * (up - down);
down = s[j].y1, up = s[j].y2;
}
else if(dcmp(s[j].y2 - up) > 0)
up = s[j].y2;
}
ans += (right[i] - left[i]) * (up - down);
}
printf("Total explored area: %.2lf\n", ans);
}
int main()
{
int t = 0;
for(;;)
{
scanf("%d", &X);
if(!X)
break;
init();
printf("Test case #%d\n", ++ t);
solve();
printf("\n");
}
return 0;
}