算法 {掃描線}

2023-11-19

算法 {掃描線}

掃描線

定義

#題目#: 二維空間上 給定若干個(凸多邊形), 求他們的並集的面積;
#思路#: 用若干個x=?(與Y軸平行的線) 將目標區域T劃分成若干個子區域Ai, 即S = A1 + A2 + ..., 然後單獨求每個Ai;

性質

與積分有點類似 都是求一個區域的面積; 他這裡是(凸多邊形)的並集, 如果是其他圖形 比如(圓) 就不適合掃描線了 需要使用(積分法: 參見辛普森積分);

算法

水平矩陣

定義

求若干個(邊長與X|Y軸平行的)矩陣的並集的面積;

代碼

{ // __ScanningLine
//< 時間為`O(N^2)`;
#define  LEF_( i)  @TODO   // 第i個矩陣的 左邊的`X`坐標;
#define  RIG_( i)  @TODO   // 第i個矩陣的 右邊的`X`坐標;
#define  LOW_( i)  @TODO   // 第i個矩陣的 下邊的`Y`坐標;
#define  HIG_( i)  @TODO   // 第i個矩陣的 上邊的`Y`坐標;
using __PosType = decltype( LEF_(0)); // 坐標的類型
    const int __N = @TODO; // 矩陣個數
    ASSERT_( __N > 0);

    vector< __PosType> Xpos( __N*2); // 所有的`LEF_(i)`從小到大排序;
    for( int i = 0; i < __N; ++i){ Xpos[ i*2] = LEF_(i), Xpos[ i*2+1] = RIG_(i);}
    sort( Xpos.begin(), Xpos.end());

    vector< int> Ind( __N); // 矩陣按照`LOW_()`從小到大排序;
    iota( Ind.begin(), Ind.end(), 0);
    sort( Ind.begin(), Ind.end(), [&]( int _a, int _b)->bool{ return LOW_(_a) < LOW_(_b);});

    Tools::Double ANS = 0;
    for( int i = 0; i+1 < __N*2; ++i){
        if( Xpos[i+1] == Xpos[i]){ continue;}
        auto l = Xpos[ i], r = Xpos[ i+1];

        __PosType preLow = 1, preMaxHig = 0; // 表示一個*非法區間*, 因為合法區間一定滿足`preLow <= preMaxHig`;
        for( int j = 0; j < __N; ++j){
            auto ind = Ind[j];
            if( LEF_(ind) <= l  &&  RIG_(ind) >= r){
                if( preLow > preMaxHig){ preLow = LOW_(ind), preMaxHig = HIG_(ind);}
                else{
                    if( LOW_(ind) <= preMaxHig){ preMaxHig = max(preMaxHig, HIG_(ind));}
                    else{
                        ANS += Tools::Double(r - l) * (preMaxHig - preLow);
                        preLow = LOW_(ind), preMaxHig = HIG_(ind);
                    }
                }
            }
        }
        if( preLow <= preMaxHig){
            ANS += Tools::Double(r - l) * (preMaxHig - preLow);
        }
    }

#undef  LEF_
#undef  RIG_
#undef  LOW_
#undef  HIG_
} // __ScanningLine

凸多邊形

定義

求若干個凸多邊形的交集, 此時 將所有{頂點 | 交點}處 都放置掃描線, 對於一個子區域l,r, (一個凸多邊形與x=l | r同時有交點)<->(該多邊形在該掃描線區域上有子區域Ai), 所有Ai並集 就是當前要求的子區域的面積;
Ai是一個旋轉了90度的梯形(三角形/矩形 也屬於是梯形), 令其底邊(即下側的腰)的坐標為(l,lly) (r,rly) 上腰的坐標為(l,lhy) (r,rhy) 一定滿足lly <= lhy, rly <= rhy;
單調性: 你會得到若干個Ai, 且任意兩個 都滿足: [lly_i <= lly_j]->[rly_i <= rly_j][lhy_i <= lhy_j]->[rhy_i <= rhy_j]; 也就是 對於兩個(上/下)腰(l,ly) (r,ry), ly與ry是保持單調性的, 如果ly_i <= ly_j 則也一定有ry_i <= ry_j, 否則他倆會存在交點 說明x=l | r之間 還存在一條掃描線x=m;
由於有一個單調性, 利用區間合併, 若干個有交集的梯形的並集 仍然會是梯形;

筆記

這個代碼 大概率會超時, 是因為 你獲得pot_l | pot_r所使用的Get_intersectionPoints_withLine這個函數 他的常數非常大; 如果有優化 則想辦法 在不使用該函數的前提下 獲得x=?與凸多邊形的交點;

代碼

{ // __ScanningLine
//< 時間是`O(N^3)`;
#define  CPG_( i)  A[i]  // 第i個凸多邊形`ConvexPolygon`;
    using __PosType = decltype( CPG_(0).Points[0].X);
    const int __N = N; // 凸多邊形個數
    ASSERT_( __N > 0);
    const int Eds = CPG_(0).Points.size(); // 多邊形的邊數

    vector< __PosType> Xpos; // {所有多邊形的頂點的X坐標 | 兩個多邊形的交點的X坐標} 從小到大排序;
    for( int i = 0; i < __N; ++i){
        for( int j = 0; j < Eds; ++j){
            Xpos.push_back( CPG_(i).Points[j].X);
        }
        for( int j = i + 1; j < __N; ++j){
            for( int e1 = 0; e1 < Eds; ++e1){
                for( int e2 = 0; e2 < Eds; ++e2){
                    auto ret = __Geometry2D::Segment( CPG_(i).Points[e1], CPG_(i).Points[ (e1+1)%Eds]).Get_intersectionPoint_withSegment( {CPG_(j).Points[e2], CPG_(j).Points[  (e2+1)%Eds]});
                    if( ret.first == 1){ Xpos.push_back( ret.second.X);}
                }
            }
        }
    }
    sort( Xpos.begin(), Xpos.end());

    Tools::Double ANS = 0;
    for( int i = 0; i+1 < (int)Xpos.size(); ++i){
        if( Xpos[i+1] == Xpos[i]){ continue;}
        auto LX = Xpos[ i], RX = Xpos[ i+1];

        using Interval_ = pair< pair<__PosType,__PosType>, pair<__PosType,__PosType> >;
        vector< Interval_> tpz; // `x=l|r`這兩個直線 會把一個多邊形切成一個*梯形*(與Y軸平行的梯形 他的兩個腰(不與Y軸平行)記作{下腰|上腰}) 用`{{ll,lr},{ul,ur}}`表示(下腰與`x=l|r`的交點的Y坐標為{ll|lr} 上腰與`x=l|r`的交點的Y坐標為{ul|ur}}; (`tpz: trapezoid`);
        for( int j = 0; j < __N; ++j){
            auto pot_l = CPG_( j).Get_intersectionPoints_withLine( {{LX,0}, {LX,1}});;
            auto pot_r = CPG_( j).Get_intersectionPoints_withLine( {{RX,0}, {RX,1}});
            if( pot_l.first == 0  ||  pot_r.first == 0){ continue;}
            if( pot_l.second.size() < 2){ pot_l.second.push_back( pot_l.second.back());}  sort( pot_l.second.begin(), pot_l.second.end());
            if( pot_r.second.size() < 2){ pot_r.second.push_back( pot_r.second.back());}  sort( pot_r.second.begin(), pot_r.second.end());
            tpz.push_back( { {pot_l.second[0].Y, pot_r.second[0].Y}, {pot_l.second[1].Y, pot_r.second[1].Y}});
        }
        { // 對`tpz`進行*區間合併*;
            #define  LOW_( i)  tpz[i].first
            #define  HIG_( i)  tpz[i].second
            const int __N = tpz.size();
            sort( tpz.begin(), tpz.end());
            for( int l=0, r=0; l < __N; ++r,l=r){
                auto maxHig = HIG_( l);
                while( r + 1 < __N  &&  LOW_( r+1) < maxHig){ ++r, maxHig=max( maxHig, HIG_(r));}
                ANS += ((maxHig.first - LOW_(l).first).ABS() + (maxHig.second - LOW_(l).second).ABS()) / 2 * (RX - LX);
            }
            #undef  LLOW_
            #undef  LHIG_
        }
    }

#undef  CPG_
} // __ScanningLine

例題

水平矩陣: @LINK: https://www.acwing.com/problem/content/3071/;

三角形: @LINK: https://www.acwing.com/problem/content/2803/;
目前會超時 問題完全出在求pot_l/pot_r, 即必須要避免使用Get_intersectionPoints_withLine這個函數, 需要進行優化: {將三角形的點 按照X坐標排序; @IF(X[0]<=l && X[2]>=r))[與當前掃描線有交點]-> {@IF(X[0]=X[1]=l)[說明他2個點在x=l上 另一個點在x=r上] @ELIF(X[1]=X[2]=r)[說明兩個點在x=r 另一個點在x=l] @ELSE[遍歷三條邊 他一定與x=l|r都有交點 即直接使用(直線與直線求交點) 不要使用(線段與直線求交點)] }

筆記

要求一個區域S(面積/周長), 我們將他分成若昂個獨立的部分S1,S2...(使得每個子區域裡 不包含頂點) 即S=S1+S2+..., 當然 前提是 Si比較好求 (比如他是若干個矩陣 不可以是不規則圖形 否則就太複雜了), 跟積分是一樣的 都是拆分;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

算法 {掃描線} 的相关文章

随机推荐

  • hbase与spark笔试题(选择题)

    转自 https www cnblogs com cxzdy p 5388451 html http www bigdatastudy net show aspx id 175 cid 14 一 HBASE笔试题 HBase来源于哪篇博文
  • CVS命令深入研究 zz

    CVS命令深入研究 作者 leizhimin 日期 2006 11 2 环境 Windows server 2003 sp1简体中文版 cvsnt 2 5 03 2260 msi 目录 一 CVS命令整体结构 二 CVS帮助察看方法概述 三
  • 浅谈我所见识的数据治理项目

    开篇一张图 与正文不一定有关 图片来源于朋友圈 01 写在前面 熟悉笔者的朋友可能知道 笔者之前做的并非纯数据相关工作 产品或项目 笔者属于半路出家的数据人 之前也几乎没有直接接触过数据仓库 数据中台 数据平台等产品或项目 与数据库是一直打
  • 大数据Mapreduce编程——矩阵乘法

    编程要求 完成矩阵乘法的 Map 函数和 Reduce 函数 1 设计两个矩阵 3050 50100 在每个单元格中填入一个 0 99 的随机数 并写入 两个文件中 作为 Map 函数的输入 2 测试运行矩阵乘法的 MapReduce 框架
  • Emoji表情符号用于文本情感分析-Improving sentiment analysis accuracy with emoji embedding

    Abstract Due to the diversity and variability of Chinese syntax and semantics accurately identifying and distinguishing
  • 显著性检测论文阅读整理

    1 Visual Saliency Based on Multiscale Deep Features 原文链接 https arxiv org pdf 1503 08663 pdf 翻译 https blog csdn net weixi
  • QCustomPlot获取选点坐标

    QCustomPlot版本 Version 2 1 1 设置点选择模式 customPlot gt setInteractions QCP iSelectPlottables 2 绑定点击事件 connect customPlot QCus
  • response实现文件下载(java)

    import javax servlet ServletException import javax servlet ServletOutputStream import javax servlet http HttpServlet imp
  • 阿里云多台内网ECS公用一条EIP实现访问外网

    第一步 开启ECS的ip转发功能 有公网的ECS上操作 1 vi etc sysctl conf 找到 net ipv4 ip forward 1 这一条 确保后面的值为1就行 如果没有这一条 手动加进去 保存退出 然后使用 sysctl
  • Linux内核源码学习(1)

    一 内核简介 1 在安装好的Linux系统中 内核的源代码位于 usr src linux 2的10次方就是1K 1024 16位CPU的地址空间是64K X86结构的80386是32位CPU 段描述结构伪代码 typedef struct
  • java.lang.Long cannot be cast to java.lang.Integer解决办法

    Integer属于不可更改类型 而且Long和Integer没有任何继承关系 当然不能这样转换 例如 public Integer getUsersCount String hql select count from Users List
  • vscode实现文件单步调试保姆级教程

    第一步 第二步 第三步 第四步 第五步 第六步 第七步 第八步 第九步 第十步 点击终端 gt 配置任务 第十一步 第十二步 第十三步 第十四步 设置完毕 可以在源程序打断点按F5执行了
  • Spring中Quartz的详细配置

    关于cron表达式 来自网络 Cron 表达式包括以下 7 个字段 秒 分 小时 月内日期 月 周内日期 年 可选字段 特殊字符 Cron 触发器利用一系列特殊字符 如下所示 反斜线 字符表示增量值 例如 在秒字段中 5 15 代表从第 5
  • 常见状态码

    SWITCH PROTOCOL 101 Switching Protocols OK 200 OK CREATED 201 Created ACCEPTED 202 Accepted NO CONTENT 204 No Content PA
  • 雷军 1994 年写的代码,经典老古董~

    整合整理 程序员的那些事 id iProgrammer 雷军的代码像诗一样优雅 有些网友在评论中质疑 说雷军代码不会是 屎 一样优雅吧 说这话的网友 也许是开玩笑的 也许是真没看过雷军写过的代码 在 2011 年的时候 我们在微博转过雷军在
  • 《计算机网络》——第四章知识点

    1 终点不可达 当路由器或主机不能交付数据报时就向源点发送终点不可达报文 无法交付 源点抑制 当路由器或主机由于拥塞而丢弃数据报时 就向源点发送源点抑制报文 使源点知道应当把数据报的发送速率放慢 拥塞丢数据 3 时间超过 当路由器收到生存时
  • [UE5蓝图基础一]13.类似”人类一败涂地”掉落一定距离会回到空中 最终着落点还是设定地形上

    利用合体触发器Box Conllision 目标点 在放置actor里 实现 修改盒体范围为2W 当人物与盒子重叠就瞬移到空中
  • 苹果美区app内购方法及经验

    苹果美区app内购方法及经验 方法一 礼品卡 失败 淘宝or亚马逊购买苹果礼品卡 经测试账号充值成功 也可以购买付费app 但内购会经典限购 等了n周依然没有解除 如果养号成功大概就没什么问题 但苯人等不及了 方法二 美区转国区 实在太想氪
  • 编译安装mysql5.7.10

    1 gt cmake MySQL使用cmake跨平台工具预编译源码 用于设置mysql的编译参数 如 安装目录 数据存放目录 字符编码 排序规则等 安装最新版本即可 2 gt make3 75 mysql源代码是由C和C 语言编写 在Lin
  • 算法 {掃描線}

    算法 掃描線 掃描線 定義 題目 二維空間上 給定若干個 凸多邊形 求他們的並集的面積 思路 用若干個x 與Y軸平行的線 將目標區域T劃分成若干個子區域Ai 即S A1 A2 然後單獨求每個Ai 性質 與積分有點類似 都是求一個區域的面積