Pick 定理 揭示了格點多邊形面積與內部/邊界格點數的精確關係:
S = N + L/2 - 1
這一定理將連續幾何問題轉化為離散計數問題,是解決格點幾何題目的利器。
給定兩個格點 A(x₀,y₀)、B(x₁,y₁),線段 AB 上的格點數等於 gcd(|x₁-x₀|, |y₁-y₀|)。
數學原理:線段參數方程 x = x₀ + λ(x₁-x₀), y = y₀ + λ(y₁-y₀),λ ∈ [0,1]。要使 x,y 同時為整數,λ 的分母必須是 Δx、Δy 的公約數,故格點數 = gcd(Δx, Δy) + 1(含端點)。三角形三邊疊加時頂點被重複計算,總邊界點數 L = Σ gcd(Δxᵢ, Δyᵢ)。
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
int OnEdge(int n, POINT *p) {
int ret = 0;
for (int i = 0; i < n; i++) {
int dx = abs(p[i].x - p[(i+1)%n].x);
int dy = abs(p[i].y - p[(i+1)%n].y);
ret += gcd(dx, dy);
}
return ret;
}
三角形面積的 2 倍 = 向量叉積絕對值:
int Int_area(POINT a, POINT b, POINT c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
問題:給定三角形三頂點(均為格點),求嚴格內部的格點數(不含邊界)。
推導:由 Pick 定理 S = N + L/2 - 1 得 N = S - L/2 + 1。因程序中 area = 2S,故 N = (area - L + 2) / 2。
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef struct Point { int x, y; } POINT;
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
int Int_area(POINT a, POINT b, POINT c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
int edgenum(POINT a, POINT b) {
int dx = abs(a.x - b.x), dy = abs(a.y - b.y);
return gcd(dx, dy);
}
int main() {
POINT a, b, c;
while (cin >> a.x >> a.y >> b.x >> b.y >> c.x >> c.y) {
if (!a.x && !a.y && !b.x && !b.y && !c.x && !c.y) break;
int area = abs(Int_area(a, b, c)); // 2倍面積
int OnEdge = edgenum(a,b) + edgenum(b,c) + edgenum(c,a);
int InSide = (area - OnEdge + 2) / 2; // Pick定理反推內部點數
cout << InSide << endl;
}
return 0;
}
| 輸入 | 三角形頂點 | 面積×2 | 邊界點數 | 內部點數 |
|---|---|---|---|---|
| 0 0 1 0 0 1 | (0,0)-(1,0)-(0,1) | 1 | 3 | 0 |
| 0 0 5 0 0 5 | (0,0)-(5,0)-(0,5) | 25 | 15 | 6 |