1. 程式人生 > 實用技巧 >【計算幾何 05】Pick定理

【計算幾何 05】Pick定理

什麼是Pick定理(皮克定理)

來自wiki的介紹:

給定頂點座標均是整點(或正方形格子點)的簡單多邊形,皮克定理說明了其面積 \(A\)和內部格點數目 \(i\) 、邊上格點數目 \(b\) 的關係:\(A = i + \frac b 2 - 1\)

因為所有簡單多邊形都可切割為一個三角形和另一個簡單多邊形。考慮一個簡單多邊形 \(P\),及跟\(P\)有一條共同邊的三角形\(T\)。若\(P\) 符合皮克公式,則只要證明\(P\)加上\(T\)\(PT\)亦符合皮克公式(I),與及三角形符合皮克公式(II),就可根據數學歸納法,對於所有簡單多邊形皮克公式都是成立的。

詳細證明:Click Here

Pick定理有以下推廣:

  • 取格點的組成圖形的面積為一單位。在平行四邊形格點,皮克定理依然成立。套用於任意三角形格點,皮克定理則是 \({\displaystyle A=2 \times i+b-2}\)
  • 對於非簡單的多邊形 \({\displaystyle P}\) ,皮克定理 \({\displaystyle A=i+{\frac {b}{2}}-\chi (P)}\) ,其中 \({\displaystyle \chi (P)}\) 表示 \({\displaystyle P}\)尤拉特徵數
  • 高維推廣:Ehrhart 多項式
  • 皮克定理和 尤拉公式\({\displaystyle V-E+F=2}\)
    )等價。

一道例題 (POJ 1265)

題目大意

給一個平面上的簡單多邊形,求邊上的點,多邊形內的點,多邊形面積。

Solution

這道題目其實用了以下三個知識:

  • 以格子點為頂點的線段,覆蓋的點的個數為 \(\gcd(dx,dy)\) ,其中, \(dx,dy\) 分別為線段橫向佔的點數和縱向佔的點數。如果 \(dx\)\(dy\)\(0\) ,則覆蓋的點數為 \(dy\) \(dx\)
  • Pick 定理:平面上以格子點為頂點的簡單多邊形的面積 = 邊上的點數/2 + 內部的點數 - 1。
  • 任意一個多邊形的面積等於按順序求相鄰兩個點與原點組成的向量的叉積之和(這個也可以通過順時針定積分求得)。

於是這題就愉快地做完了

// Author : RioTian
// Time : 20/10/21
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 100 + 10;
struct node {
    int x, y;
} p[N];
inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
inline int area(int a, int b) { return p[a].x * p[b].y - p[a].y * p[b].x; }
int main() {
    // freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t, ncase = 1;
    cin >> t;
    while (t--) {
        int n, dx, dy, x, y, num = 0, sum = 0;
        cin >> n;
        p[0].x = p[0].y = 0;
        for (int i = 1; i <= n; ++i) {
            cin >> x >> y;
            p[i].x = x + p[i - 1].x, p[i].y = y + p[i - 1].y;
            dx = x, dy = y;
            if (x < 0) dx = -x;
            if (y < 0) dy = -y;
            num += gcd(dx, dy);
            sum += area(i - 1, i);
        }
        if (sum < 0) sum = -sum;
        if (sum < 0) sum = -sum;
        printf("Scenario #%d:\n", ncase++);
        printf("%d %d %.1f\n\n", (sum - num + 2) >> 1, num, sum * 0.5);
    }
}