zoukankan      html  css  js  c++  java
  • poj 1177 picture

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

  • 相关阅读:
    PAIP.MYSQL数据库比较
    paip.验证码识别----判断汉字还是英文
    SQLServer2008客户端软件
    paip.多个TOMCAT共存在一台主机上配置方法
    paip.银行卡号的发卡行归属地查询
    paip.获取当前实际北京时间API
    PAIP.HIBERNATE ORA02289 sequence does not exist的解决
    C51与汇编语言混合编程之一
    KEIL C51高级编程之二
    可重入函数与不可重入函数(转)
  • 原文地址:https://www.cnblogs.com/kylinsky/p/5049928.html
Copyright © 2011-2022 走看看