题目链接: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,讲解非常详细

 

posted on 2015-12-15 23:53  西湖小鲤鱼  阅读(144)  评论(0编辑  收藏  举报