題目連接:Here函數
題意:spa
給定 \(n\) 個點的座標,先問這些點可否組成一個凸包,如是凸包,問用不相交的線來切這個凸包使得凸包只由三角形組成,根據 \(cost_{i, j} = |x_i + x_j| * |y_i + y_j| \% p\)算切線的費用,問最少的切割費用。.net
解題思路:參考於 ZeroClock,感謝!code
經典的最優三角剖分模型加一點計算幾何的知識。blog
先判斷是否爲凸包,這個排個序就好弄,搬了一下凸包函數排序的板子。排序
返回凸包中的頂點數量再與 \(n\) 比較。ci
這一步處理完以後就是用 \(n-3\) 條直線將凸包切成 \(n-2\) 個三角形。get
咱們要切的是以 \(1\) 和 \(n\) 爲起始點的凸包,因爲切線不能相交,那麼選擇 \(1\) 點和 \(n\) 點必有另一點 \(S\) 要和它們組成一個三角形,而後凸包被分紅三個部分: \(k_0,k_1,k_2\) ,而後把 \(k_1\) 當作一個以 \(n\) 點 \(S\) 點位起始點的凸包,是否是又能夠用相同的方法處理這個凸包呢?答案是確定,就是這樣慢慢地將凸包分紅一個個子凸包計算費用,最後再更新到點 \(1\) 和點 \(n\) 爲起始點的凸包。it
模擬上面的過程,設 \(Dp[i][j]\) 爲以 \(i\) 爲起點,\(j\) 爲終點的凸包被切割成一個個小三角形所須要的費用。io
那麼
爲連一條 \(i\) 到 \(k\) 的線的費用),由於 \(dp[i][j]\) 只表示爲以 \(i\) 爲起點,\(j\) 爲終點的凸包內部被切割的費用,因此在連線的時候能夠加上邊界費用而不算重複計算。
const int N = 1e3 + 10, inf = 1e9; struct Point { int x, y; } p[N]; int cost[N][N], n, m; int f[N][N]; int abs(int x) {return x < 0 ? -x : x;} int xmul(Point p1, Point p2, Point p0) { return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y); } bool cmp(const Point &a, const Point &b) { if (a.y == b.y)return a.x < b.x; return a.y < b.y; } Point save[400], temp[400]; int Graham(Point *p, int n) { sort(p, p + n, cmp); save[0] = p[0]; save[1] = p[1]; int top = 1; for (int i = 0; i < n; ++i) { while (top && xmul(save[top], p[i], save[top - 1]) >= 0) top --; save[++top] = p[i]; } int mid = top; for (int i = n - 2; i >= 0; --i) { while (top > mid && xmul(save[top], p[i], save[top - 1]) >= 0) top --; save[++top] = p[i]; } return top; } int Count(Point a, Point b) { return (abs(a.x + b.x) * abs(a.y + b.y)) % m; } int main() { cin.tie(nullptr)->sync_with_stdio(false); while (cin >> n >> m) { for (int i = 0; i < n; ++i) cin >> p[i].x >> p[i].y; int tot = Graham(p, n); // 求凸包 if (tot < n) {cout << "I can't cut.\n"; continue;} memset(cost, 0, sizeof(cost)); // for (int i = 0; i < N; ++i) for (int j = 0; j < N; ++j)cost[i][j] = 0; for (int i = 0; i < n; ++i) for (int j = i + 2; j < n; ++j) cost[j][i] = cost[i][j] = Count(save[i], save[j]); for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) f[i][j] = inf; f[i][(i + 1) % n] = 0; } for (int i = n - 3; i >= 0; --i) // 注意三個for循環順序 for (int j = i + 2; j < n; ++j) //由於要保證在算dp[i][j]時dp[i][k]和dp[k][j]時已經計算,因此i爲逆序,j要升序 for (int k = i + 1; k <= j - 1; ++k) f[i][j] = min(f[i][j], f[i][k] + f[k][j] + cost[i][k] + cost[k][j]); cout << f[0][n - 1] << "\n"; } }