题目链接:http://poj.org/problem?id=1177
分析:这道题主要用到了线段树、扫描线以及离散化的相关算法。
离散化
离散化是当数字不多但是范围很大时采用的一种方法,将大区间的数字映射到一个小区间上,如,有一组数字:132398,12781,2342876,232,将其离散化后将得到2,1,3,0.
离散化的算法:
1 //y为大小为n的数组,存放着离散化前的数 2 sort(y,y+n); 3 int unique_count=unique(y,y+n)-y; 4 //find_i为自己编写的根据N来寻找对应的i的函数 5 find_i(N);
将不同的y值离散化后建立线段树,下面为树的节点,其中,left,right为离散化的区间值,左闭右开,count为该区间被覆盖的次数,初始化为0,inter为竖直的矩形边的区间数,之后计算平行于x轴的边的周长会用到,lflag、rflag为左右端点是否被覆盖,用于计算inter,len为被覆盖长度。
struct Node{ int left, right; int count; int inter; int lflag, rflag; int len; };
扫描线
将垂直于x轴的矩形的边作为扫描线,对其按x大小排序后逐个进行扫描,flag为0表示右边的边,flag为1表示左边的边。
struct Scan{ int x; int y1, y2; int flag; };
代码如下:
1 #include <iostream> 2 #include <algorithm> 3 #include <cmath> 4 5 #define MAX 10000 6 7 using namespace std; 8 9 struct Scan{ 10 int x; 11 int y1, y2; 12 int flag; 13 }; 14 struct Node{ 15 int left, right; 16 int count;//被覆盖次数 17 int inter;////覆盖后的区间数量,2*line*| | 18 int lflag, rflag;//左右端点是否被覆盖 19 int len;//测度,覆盖区间的长度 20 }; 21 22 struct Node node[4 * MAX]; 23 struct Scan scanline[2 * MAX]; 24 int y[MAX]; 25 26 bool cmp(struct Scan line1, struct Scan line2); 27 void build(int L, int R, int i); 28 void insert(int L, int R, int i); 29 void remove(int L, int R, int i); 30 void update_len(int i); 31 void update_inter(int i); 32 33 int main(){ 34 int N; 35 //freopen("picture_in.txt", "r", stdin); 36 cin >> N; 37 int x1, x2, y1, y2; 38 int len_now = 0, inter_now = 0; 39 int total_perimeter = 0; 40 int n = 0; 41 while (N--){ 42 cin >> x1 >> y1 >> x2 >> y2; 43 y[n] = y1; 44 scanline[n].x = x1; 45 scanline[n].y1 = y1; 46 scanline[n].y2 = y2; 47 scanline[n++].flag = 1; 48 y[n] = y2; 49 scanline[n].x = x2; 50 scanline[n].y1 = y1; 51 scanline[n].y2 = y2; 52 scanline[n++].flag = 0; 53 } 54 sort(y, y + n); 55 sort(scanline, scanline + n, cmp); 56 //y数组中不重复的个数 57 int unique_count = unique(y, y + n) - y; 58 build(0, unique_count - 1, 0); 59 for (int i = 0; i<n; i++){ 60 if (scanline[i].flag) 61 //左边插入 62 insert(scanline[i].y1, scanline[i].y2, 0); 63 //右边消除 64 else 65 remove(scanline[i].y1, scanline[i].y2, 0); 66 if (i>0){ 67 total_perimeter += 2 * (scanline[i].x - scanline[i - 1].x)*inter_now; 68 } 69 total_perimeter += abs(len_now - node[0].len); 70 //cout << "perimeter after y:" << total_perimeter << endl; 71 len_now = node[0].len; 72 inter_now = node[0].inter; 73 //cout << "len_now:" << len_now << " inter_now:" << inter_now << endl << endl; 74 } 75 cout << total_perimeter << endl; 76 return 0; 77 } 78 79 bool cmp(struct Scan line1, struct Scan line2){ 80 if (line1.x == line2.x) 81 return line1.flag>line2.flag; 82 return line1.x<line2.x; 83 } 84 85 //创建指定区间的线段树并初始化 86 void build(int L, int R, int i){ 87 node[i].left = L; 88 node[i].right = R; 89 node[i].count = 0; 90 node[i].inter = 0; 91 node[i].len = 0; 92 node[i].lflag = node[i].rflag = 0; 93 if (R - L>1){ 94 int M = (L + R) / 2; 95 build(L, M, 2 * i + 1); 96 build(M, R, 2 * i + 2); 97 } 98 } 99 100 //左边的边出现,插入记录 101 //不管是插入还是消除,都一直更新到根结点,即node[0] 102 void insert(int L, int R, int i){ 103 if (L <= y[node[i].left] && R >= y[node[i].right]) 104 node[i].count++; 105 else if (node[i].right - node[i].left == 1) 106 return; 107 else{ 108 int mid = (node[i].left + node[i].right) / 2; 109 if (R <= y[mid]) 110 insert(L, R, 2 * i + 1); 111 else if (L >= y[mid]) 112 insert(L, R, 2 * i + 2); 113 else{ 114 insert(L, y[mid], 2 * i + 1); 115 insert(y[mid], R, 2 * i + 2); 116 } 117 } 118 update_len(i); 119 update_inter(i); 120 } 121 122 //右边的边出现,则消除记录 123 //其实就是一步步退回去 124 void remove(int L, int R, int i){ 125 if (L <= y[node[i].left] && R >= y[node[i].right]) 126 node[i].count--; 127 else if (node[i].right - node[i].left == 1) 128 return; 129 else{ 130 int mid = (node[i].left + node[i].right) / 2; 131 if (R <= y[mid]) 132 remove(L, R, 2 * i + 1); 133 else if (L >= y[mid]) 134 remove(L, R, 2 * i + 2); 135 else{ 136 remove(L, y[mid], 2 * i + 1); 137 remove(y[mid], R, 2 * i + 2); 138 } 139 } 140 update_len(i); 141 update_inter(i); 142 } 143 144 void update_len(int i){ 145 if (node[i].count>0) 146 node[i].len = y[node[i].right] - y[node[i].left]; 147 else if (node[i].right - node[i].left == 1) 148 node[i].len = 0; 149 else 150 node[i].len = node[2 * i + 1].len + node[2 * i + 2].len; 151 } 152 153 void update_inter(int i){ 154 if (node[i].count>0){ 155 node[i].lflag = 1; 156 node[i].rflag = 1; 157 node[i].inter = 1; 158 } 159 else if (node[i].right - node[i].left == 1){ 160 node[i].lflag = 0; 161 node[i].rflag = 0; 162 node[i].inter = 0; 163 } 164 else{ 165 node[i].lflag = node[2 * i + 1].lflag; 166 node[i].rflag = node[2 * i + 2].rflag; 167 node[i].inter = node[2 * i + 1].inter + node[2 * i + 2].inter - 168 node[2 * i + 1].rflag*node[2 * i + 2].lflag; 169 } 170 }
给几组测试数据:
测试数据1:
输入:
47
-1105 -1155 -930 -285
-765 -1115 -615 -375
-705 -480 -165 -285
-705 -1200 -175 -1025
-275 -1105 -105 -385
-10 -1165 185 -285
315 -1160 400 -710
340 -1195 655 -1070
580 -1140 655 -265
325 -480 395 -335
365 -390 620 -265
365 -770 610 -665
815 -1195 1110 -1070
825 -760 1100 -660
810 -405 1115 -275
780 -700 860 -360
1065 -695 1130 -360
775 -1110 860 -735
1070 -1110 1145 -730
-1065 -95 140 260
-725 80 750 460
135 -135 490 840
135 -135 490 750
-520 40 -210 945
-595 620 215 695
670 -5 855 610
550 -75 830 -25
815 240 1085 370
980 -90 1125 145
280 150 490 315
-1035 -155 -845 -90
855 815 950 1030
785 980 860 1165
945 985 1015 1160
730 835 1075 895
875 695 935 790
-1165 420 -520 650
-1090 815 -210 945
-130 800 65 1160
120 980 690 1150
-1140 995 -125 1180
-825 1050 -195 1135
-90 865 10 1090
280 1045 625 1090
-655 1065 -245 1115
-1155 70 -790 315
-1005 110 -825 225
输出:3700
测试数据2:
输入:
2
-10 -10 0 10
0 -10 10 10
输出:
80
代码是参考另一位大牛写的,原文请见:http://www.cnblogs.com/shuaiwhu/archive/2012/04/22/2464876.html,讲解非常详细