簡體   English   中英

指向多邊形與軸平行的多邊形內部

[英]point inside a polygon with edges parallel to the axes

我已經在viewstreetstreet解決了多邊形問題,但是它似乎太慢了。 解決該問題的最佳方法是什么?

XY平面上有N個具有整數坐標(xi,yi)的點。 您將獲得一組多邊形,其所有邊緣均與軸平行(換句話說,多邊形的所有角度均為90度角,並且所有線都在基本方向上。沒有對角線)。 對於每個多邊形,程序應找到位於其內部的點數(位於多邊形邊界上的點也被視為位於多邊形內部)。

輸入:

第一行包含兩個整數N和Q。下一行包含N個以空格分隔的整數坐標(xi,yi)。 Q查詢跟隨。 每個查詢由第一行中的單個整數Mi組成,后跟由Mi分隔的整數坐標(x [i] [j],y [i] [j]),按順時針順序指定查詢多邊形的邊界。

多邊形是垂直線段和水平線段的交替序列。 多邊形具有Mi邊,其中(x [i] [j],y [i] [j])連接到(x [i] [(j + 1)%Mi],y [i] [(j + 1 )%Mi]。對於每個0 <= j <Mi,x [i] [(j + 1)%Mi] == x [i] [j]或y [i] [(j + 1)%Mi ] == y [i] [j],但不能同時包含兩者,還可以確保多邊形不會自相交。

輸出:

對於每個查詢輸出,在單獨的行中查詢多邊形內的點數。

樣本輸入1:

16 2
0 0
0 1
0 2
0 3
1 0
1 1
1 2
1 3
2 0
2 1
2 2
2 3
3 0
3 1
3 2
3 3
8
0 0
0 1
1 1
1 2
0 2
0 3
3 3
3 0
4
0 0
0 1
1 1
1 0

樣本輸出1:

16
4

在此處輸入圖片說明

樣本輸入2:

6 1
1 1
3 3
3 5
5 2
6 3
7 4
10
1 3
1 6
4 6
4 3
6 3
6 1
4 1
4 2
3 2
3 3

樣本輸出2:

4

在此處輸入圖片說明

限制條件:

1 <= N <= 20,000 1 <= Q <= 20,000 4 <= Mi <= 20每個坐標的值最多為200,000

我對上述語言或偽代碼的解決方案感興趣。

編輯:這是我的代碼,但它是O(n ^ 2)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Polygon
{
// avoding System.Drawing dependency
public struct Point
{
    public int X { get; private set; }
    public int Y { get; private set; }

    public Point(int x, int y)
        : this()
    {
        X = x;
        Y = y;
    }

    public override int GetHashCode()
    {
        return X ^ Y;
    }

    public override bool Equals(Object obj)
    {
        return obj is Point && this == (Point)obj;
    }

    public static bool operator ==(Point a, Point b)
    {
        return a.X == b.X && a.Y == b.Y;
    }

    public static bool operator !=(Point a, Point b)
    {
        return !(a == b);
    }
}

public class Solution
{
    static void Main(string[] args)
    {
        BasicTestCase();
        CustomTestCase();
        // to read from STDIN
        //string firstParamsLine = Console.ReadLine();
        //var separator = new char[] { ' ' };
        //var firstParams = firstParamsLine.Split(separator);
        //int N = int.Parse(firstParams[0]);
        //int Q = int.Parse(firstParams[1]);

        //List<Point> points = new List<Point>(N);

        //for (int i = 0; i < N; i++)
        //{
        //    var coordinates = Console.ReadLine().Split(separator);
        //    points.Add(new Point(int.Parse(coordinates[0]), int.Parse(coordinates[1])));
        //}

        //var polygons = new List<List<Point>>(Q); // to reduce realocation
        //for (int i = 0; i < Q; i++)
        //{

        //    var firstQ = Console.ReadLine().Split(separator);
        //    int coordinatesLength = int.Parse(firstQ[0]);
        //    var polygon = new List<Point>(coordinatesLength);
        //    for (int j = 0; j < coordinatesLength; j++)
        //    {
        //        var coordinates = Console.ReadLine().Split(separator);
        //        polygon.Add(new Point(int.Parse(coordinates[0]), int.Parse(coordinates[1])));
        //    }
        //    polygons.Add(polygon);
        //}

        //foreach (var polygon in polygons)
        //{
        //    Console.WriteLine(CountPointsInPolygon(points, polygon));
        //}


    }

    private static void BasicTestCase()
    {
        List<Point> points = new List<Point>(){ new Point(0, 0),
                                                new Point(0, 1),
                                                new Point(0, 2),
                                                new Point(0, 3),
                                                new Point(1, 0),
                                                new Point(1, 1),
                                                new Point(1, 2),
                                                new Point(1, 3),
                                                new Point(2, 0),
                                                new Point(2, 1),
                                                new Point(2, 2),
                                                new Point(2, 3),
                                                new Point(3, 0),
                                                new Point(3, 1),
                                                new Point(3, 2),
                                                new Point(3, 3) };

        List<Point> polygon1 = new List<Point>(){   new Point(0, 0),
                                                    new Point(0, 1),
                                                    new Point(2, 1),
                                                    new Point(2, 2),
                                                    new Point(0, 2),
                                                    new Point(0, 3),
                                                    new Point(3, 3),
                                                    new Point(3, 0)};

        List<Point> polygon2 = new List<Point>(){   new Point(0, 0),
                                                    new Point(0, 1),
                                                    new Point(1, 1),
                                                    new Point(1, 0),};

        Console.WriteLine(CountPointsInPolygon(points, polygon1));
        Console.WriteLine(CountPointsInPolygon(points, polygon2));

        List<Point> points2 = new List<Point>(){new Point(1, 1),
                                                new Point(3, 3),
                                                new Point(3, 5),
                                                new Point(5, 2),
                                                new Point(6, 3),
                                                new Point(7, 4),};

        List<Point> polygon3 = new List<Point>(){   new Point(1, 3),
                                                    new Point(1, 6),
                                                    new Point(4, 6),
                                                    new Point(4, 3),
                                                    new Point(6, 3),

                                                    new Point(6, 1),
                                                    new Point(4, 1),
                                                    new Point(4, 2),
                                                    new Point(3, 2),
                                                    new Point(3, 3),};

        Console.WriteLine(CountPointsInPolygon(points2, polygon3));
    }

    private static void CustomTestCase()
    {
        // generated 20 000 points and polygons
        using (StreamReader file = new StreamReader(@"in3.txt"))
        {
            string firstParamsLine = file.ReadLine();
            var separator = new char[] { ' ' };
            var firstParams = firstParamsLine.Split(separator);
            int N = int.Parse(firstParams[0]);
            int Q = int.Parse(firstParams[1]);

            List<Point> pointsFromFile = new List<Point>(N);

            for (int i = 0; i < N; i++)
            {
                var coordinates = file.ReadLine().Split(separator);
                pointsFromFile.Add(new Point(int.Parse(coordinates[0]), int.Parse(coordinates[1])));
            }

            var polygons = new List<List<Point>>(Q); // to reduce realocation
            for (int i = 0; i < Q; i++)
            {

                var firstQ = file.ReadLine().Split(separator);
                int coordinatesLength = int.Parse(firstQ[0]);
                var polygon = new List<Point>(coordinatesLength);
                for (int j = 0; j < coordinatesLength; j++)
                {
                    var coordinates = file.ReadLine().Split(separator);
                    polygon.Add(new Point(int.Parse(coordinates[0]), int.Parse(coordinates[1])));
                }
                polygons.Add(polygon);
            }

            foreach (var polygon in polygons)
            {
                Console.WriteLine(CountPointsInPolygon(pointsFromFile, polygon));
            }

        }
    }

    public static int CountPointsInPolygon(List<Point> points, List<Point> polygon)
    {
        // TODO input check
        polygon.Add(polygon[0]); // for simlicity
        // check if any point is outside of the bounding box of the polygon
        var minXpolygon = polygon.Min(p => p.X);
        var maxXpolygon = polygon.Max(p => p.X);
        var minYpolygon = polygon.Min(p => p.Y);
        var maxYpolygon = polygon.Max(p => p.Y);
        // ray casting algorithm (form max X moving to point)
        int insidePolygon = 0;
        foreach (var point in points)
        {
            if (point.X >= minXpolygon && point.X <= maxXpolygon && point.Y >= minYpolygon && point.Y <= maxYpolygon)
            {       // now points are inside the bounding box 
                    isPointsInside(polygon, point, ref insidePolygon);
            } // else outside
        }
        return insidePolygon;

    }
    private static void isPointsInside(List<Point> polygon, Point point, ref int insidePolygon)
    {
        int intersections = 0;    

        for (int i = 0; i < polygon.Count - 1; i++)
        {
            if (polygon[i] == point)
            {
                insidePolygon++;
                return;
            }
            if (point.isOnEdge(polygon[i], polygon[i + 1]))
            {
                insidePolygon++;
                return;
            }
            if (Helper.areIntersecting(polygon[i], polygon[i + 1], point))
            {
                intersections++;
            }
        }

        if (intersections % 2 != 0)
        {
            insidePolygon++;
        }   
    }
}

static class Helper
{
    public static bool isOnEdge(this Point point, Point first, Point next)
    {
            // onVertical 
            if (point.X == first.X && point.X == next.X && point.Y.InRange(first.Y, next.Y))
            {
                return true;
            }
            //onHorizontal 
            if (point.Y == first.Y && point.Y == next.Y && point.X.InRange(first.X, next.X))
            {
                return true;
            }
            return false;
    }

    public static bool InRange(this int value, int first, int second)
    {
        if (first <= second)
        {
            return value >= first && value <= second;
        }
        else
        {
            return value >= second && value <= first;
        }
    }

    public static bool areIntersecting(Point polygonPoint1, Point polygonPoint2, Point vector2End)
    {
        // "move" ray up for 0.5 to avoid problem with parallel edges
        if (vector2End.X < polygonPoint1.X )
        {
            var y = (vector2End.Y + 0.5);
            var first = polygonPoint1.Y;
            var second = polygonPoint2.Y;
            if (first <= second)
            {
                return y >= first && y <= second;
            }
            else
            {
                return y >= second && y <= first;
            }
        }
        return false;
    }
}

}

更快的解決方案是將這些點放入四叉樹中

區域四叉樹可能更容易編碼,但是點四叉樹可能更快。 如果使用區域四叉樹,則當四叉樹中的點數低於閾值(例如16點)時,可以停止對四叉樹進行細分

每個四邊形存儲它包含的點數,以及坐標列表(對於葉節點)或指向較小四邊形的指針。 (當四邊形的大小達到1時,您可以省略坐標列表,因為它們必須全部重合)

要計算多邊形內的點,請查看代表四叉樹根的最大四邊形。

  1. 將多邊形裁剪到四邊形的邊緣
  2. 如果多邊形不與四邊形重疊,則返回0
  3. 如果這是大小為1x1的四邊形,則返回四邊形中的點數
  4. 如果多邊形完全圍繞四邊形,則返回四邊形中的點數。
  5. 如果這是葉節點,則使用鉛垂線算法測試每個點
  6. 否則遞歸計算每個子四邊形中的點

(如果一個四邊形只有一個非空孩子,那么您可以跳過步驟1,2,3,4,5以更快一點)

(2和4中的測試不必完全准確)

我會嘗試將光線從底部投射到每個點,跟蹤它穿過多邊形的位置(從右至左線段)或從多邊形中返回的位置(從左至右線段)。 像這樣:

count := 0
For each point (px, py):
   inside := false
   For each query line (x0, y0) -> (x1, y1) where y0 = y1
      if inside
         if x0 <= px < x1 and py > y0
            inside = false
      else 
         if x1 <= px <= x0 and py >= y0
            inside = true
   if inside
      count++

在這兩種情況下,> vs.> =是這樣的,以便將上邊緣的一個點視為內部。 我實際上尚未對此進行編碼以查看其是否有效,但我認為這種方法是正確的。

梯形分解對您有用嗎?

我指的是約旦曲線定理鉛垂線算法

相關的偽代碼是

int crossings = 0
for (each line segment of the polygon)
    if (ray down from (x,y) crosses segment)
        crossings++;
if (crossings is odd)
    return (inside);
else return (outside);

這是作者的解決方案-有點困惑,不是嗎?

#include <iostream>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <ctime>
using namespace std;

typedef long long int64;
const int N = 100000, X = 2000000001;
const int Q = 100000, PQ = 20;
struct Point {
    int x, y, idx;
    Point(int _x = 0, int _y = 0, int _idx = 0) {
        x = _x;
        y = _y;
        idx = _idx;
    }
} arr_x[N], arr_y[N];
struct VLineSegment {
    int x, y1, y2, idx, sign;
    VLineSegment(int _x = 0, int _y1 = 0, int _y2 = 0, int _sign = 1, int _idx = 0) {
        x = _x;
        y1 = _y1;
        y2 = _y2;
        sign = _sign;
        idx = _idx;
    }
    bool operator<(const VLineSegment& v) const {
        return x < v.x;
    }
} segs[Q * PQ];

struct TreeNode {
    int idx1, idx2, cnt;
    TreeNode *left, *right;
    TreeNode() { left = right = 0; cnt = 0; }
    ~TreeNode() { if(left) delete left; if(right) delete right; }

    void update_stat() {
        cnt = left->cnt + right->cnt;
    }

    void build(Point* arr, int from, int to, bool empty) {
        idx1 = from;
        idx2 = to;
        if(from == to) {
            if(!empty) {
                cnt = 1;
            } else { 
                cnt = 0;
            }
        } else {
            left = new TreeNode();
            right = new TreeNode();
            int mid = (from + to) / 2;
            left->build(arr, from, mid, empty);
            right->build(arr, mid + 1, to, empty);
            update_stat();
        }
    }
    void update(Point& p, bool add) {
        if(p.idx >= idx1 && p.idx <= idx2) {
            if(idx1 != idx2) {
                left->update(p, add);
                right->update(p, add);
                update_stat();
            } else {
                if(add) {
                    cnt = 1;
                } else {
                    cnt = 0;
                }
            }
        }
    }
    int query(int ya, int yb) {
        int y1 = arr_y[idx1].y, y2 = arr_y[idx2].y;
        if(ya <= y1 && y2 <= yb) {
            return cnt;
        } else if(max(ya, y1) <= min(yb, y2)) {
            return left->query(ya, yb) + right->query(ya, yb);
        }
        return 0;
    }
};

bool cmp_x(const Point& a, const Point& b) {
    return a.x < b.x;
}
bool cmp_y(const Point& a, const Point& b) {
    return a.y < b.y;
}

void calc_ys(int x1, int y1, int x2, int y2, int x3, int sign, int& ya, int& yb) {
    if(x2 < x3) {
        yb = 2 * y2 - sign;
    } else {
        yb = 2 * y2 + sign;
    }
    if(x2 < x1) {
        ya = 2 * y1 + sign;
    } else {
        ya = 2 * y1 - sign;
    }
}

bool process_polygon(int* x, int* y, int cnt, int &idx, int i) {
    for(int j = 0; j < cnt; j ++) {
        //cerr << x[(j + 1) % cnt] - x[j] << "," << y[(j + 1) % cnt] - y[j] << endl;
        if(x[j] == x[(j + 1) % cnt]) {
            int _x, y1, y2, sign;
            if(y[j] < y[(j + 1) % cnt]) {
                _x = x[j] * 2 - 1;
                sign = -1;
                calc_ys(x[(j + cnt - 1) % cnt], y[j], x[j], y[(j + 1) % cnt], x[(j + 2) % cnt], sign, y1, y2);
            } else {
                _x = x[j] * 2 + 1;
                sign = 1;
                calc_ys(x[(j + 2) % cnt], y[(j + 2) % cnt], x[j], y[j], x[(j + cnt - 1) % cnt], sign, y1, y2);
            }
            segs[idx++] = VLineSegment(_x, y1, y2, sign, i);
        }
    }
}
int results[Q];

int n, q, c;

int main() {
    int cl = clock();
    cin >> n >> q;
    for(int i = 0; i < n; i ++) {
        cin >> arr_y[i].x >> arr_y[i].y;
        arr_y[i].x *= 2;
        arr_y[i].y *= 2;
    }
    int idx = 0, cnt, x[PQ], y[PQ];
    for(int i = 0; i < q; i ++) {
        cin >> cnt;
        for(int j = 0; j < cnt; j ++) cin >> x[j] >> y[j];
        process_polygon(x, y, cnt, idx, i);
    }
    sort(segs, segs + idx);
    memset(results, 0, sizeof results);
    sort(arr_y, arr_y + n, cmp_y);
    for(int i = 0; i < n; i ++) {
        arr_y[i].idx = i;
        arr_x[i] = arr_y[i];
    }
    sort(arr_x, arr_x + n, cmp_x);
    TreeNode tleft;
    tleft.build(arr_y, 0, n - 1, true);
    for(int i = 0, j = 0; i < idx; i ++) {
        for(; j < n && arr_x[j].x <= segs[i].x; j ++) {
            tleft.update(arr_x[j], true);
        }
        int qcnt = tleft.query(segs[i].y1, segs[i].y2);
        //cerr << segs[i].x * 0.5 << ", " << segs[i].y1 * 0.5 << ", " << segs[i].y2 * 0.5 << " = " << qcnt << " * " << segs[i].sign << endl;
        results[segs[i].idx] += qcnt * segs[i].sign;
    }
    for(int i = 0; i < q; i ++) {
        cout << results[i] << endl;
    }
    cerr << (clock() - cl) * 0.001 << endl;
    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM