題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6631
題意:共\(T\)組數據,每組數據給出\(n\)個點的坐標,這\(n\)個點按順序給出,相鄰的點相連后構成一個簡單多邊形。詢問能否在最多移動一個點的情況下新構成的圖形為軸對稱圖形。
分析:如下圖所示,一個軸對稱圖形的對稱軸僅存在兩種情況,一是相鄰兩點的中垂線,二是相隔一點的中垂線。
因此我們只需要對相鄰點和相隔一點的點對求中垂線,分別判斷是否能夠僅移動一個點使得圖形成為軸對稱圖形即可。如下圖,多邊形\(ABCDX\)可以轉變為\(ABCDE\),多邊形\(A_1B_1C_1YE_1\)可以轉變為\(A_1B_1C_1D_1E_1\)。因此判定能否構成軸對稱圖形的條件即為軸線兩側的對應點對不關於軸線對稱的數量是否\(<2\)。如下圖的\(ABCDX\)僅具有一組點對\((X,X)\)不對稱。
接下來我們需要解決的問題就是如何快速判定點對關於中垂線對稱。如下圖,若點對關於軸線對稱,則必有兩組點對的中點連線與這兩組點的連線相互垂直。如下圖若點\(N,Q\)關於點\(O,P\)形成的中垂線對稱,則應有\(ST\bot NQ\)以及\(TS\bot OP\)(\(T\),\(S\)分別為中點)。
於是我們就得到了一個\(o(n^2)\)的解法,本題就輕松解決了。(大霧)
事實上這樣是無法通過的,因為我們忽略了圖形有可能自交的情況。如下圖樣例。
如圖,\((D, E)\)點對不構成對稱,若將\(D\)點移至\(D_1\)點,則該多邊形自交,不再構成簡單多邊形。因此我們在點對不對稱時需要加上特判。特判的方法可由下圖觀察得到:
我們以對稱軸為界將多邊形分成兩部分。觀察不對稱的點對(紅點),並且將紅點與它相鄰的兩個點看作一個整體,當且僅當這兩組點都跨過對稱軸時多邊形自交(跨過對稱軸包括紅點落在對稱軸上的情況)。
AC代碼:
#include <bits/stdc++.h>
#define SIZE 1007
#define rep(i, a, b) for(int i = a; i <= b; ++i)
using namespace std;
typedef long long ll;
int t, n;
struct Point {
double x, y;
}p[SIZE], a, b, tp, mid, mx, mid2, nullp;
void io() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
}
double cross(Point a, Point b, Point c) { return (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x); }
double cdot(Point a, Point b, Point c) { return (b.x - a.x) * (b.x - c.x) + (b.y - c.y) * (b.y - a.y); }
int spjudge(int j, int k, Point mid, Point mid2) {
bool f1 = false, f2 = false; int num = 0;
if (cross(mid, p[j], mid2)) {
int pre = j - 1, post = j + 1;
if (pre < 1) pre += n;
if (post > n) post -= n;
if (cross(mid, p[pre], mid2)*cross(mid, p[post], mid2) < 0) f1 = true;
if (cross(mid, p[pre], mid2)*cross(mid, p[j], mid2) < 0) f1 = true;
if (cross(mid, p[post], mid2)*cross(mid, p[j], mid2) < 0) f1 = true;
}
else f1 = true;
if (cross(mid, p[k], mid2)) {
int pre = k + 1, post = k - 1;
if (pre < 1) pre += n;
if (post > n) post -= n;
if (cross(mid, p[pre], mid2)*cross(mid, p[post], mid2) < 0) f2 = true;
if (cross(mid, p[pre], mid2)*cross(mid, p[j], mid2) < 0) f2 = true;
if (cross(mid, p[post], mid2)*cross(mid, p[j], mid2) < 0) f2 = true;
}
else f2 = true;
if (f1&&f2) return 1;
else return 0;
}
bool judge1() {
rep(i, 1, n) {
int num = 0;
a = p[i]; b = p[i % n + 1];
mid.x = (a.x + b.x) / 2; mid.y = (a.y + b.y) / 2;
mid2.x = mid.x + (a.y - mid.y); mid2.y = mid.y + (mid.x - a.x);
int tt = n / 2 - 1;
if (n % 2) {
tp = p[(i + n / 2) % n + 1];
if (cross(mid, tp, mid2)) ++num;
}
int j = i - 1, k = i + 2;
while (tt--) {
if (j < 1) j += n;
if (k > n) k -= n;
mx.x = (p[j].x + p[k].x) / 2;
mx.y = (p[j].y + p[k].y) / 2;
if ((cdot(p[i], mid, mx) != 0) || (cdot(mid, mx, p[j]) != 0)) {
++num;
num += spjudge(j, k, mid, mid2);
}
--j, ++k;
}
if (num <= 1) return true;
}
return false;
}
bool judge2() {
rep(i, 1, n) {
int num = 0;
a = p[i]; b = p[(i + 1) % n + 1];
mid.x = (a.x + b.x) / 2; mid.y = (a.y + b.y) / 2;
mid2.x = mid.x + (a.y - mid.y); mid2.y = mid.y + (mid.x - a.x);
int tt = n / 2 - 1;
tp = p[i % n + 1];
if (cross(mid, tp, mid2)) ++num;
if (n % 2 == 0) {
tp = p[(i + n / 2) % n + 1];
if (cross(mid, tp, mid2)) ++num;
}
int j = i - 1, k = i + 3;
while (tt--) {
if (j < 1) j += n;
if (k > n) k -= n;
mx.x = (p[j].x + p[k].x) / 2;
mx.y = (p[j].y + p[k].y) / 2;
if ((cdot(p[i], mid, mx) != 0) || (cdot(mid, mx, p[j]) != 0)) {
++num;
num += spjudge(j, k, mid, mid2);
}
--j, ++k;
}
if (num <= 1) return true;
}
return false;
}
int main() {
io(); cin >> t;
while (t--) {
cin >> n;
rep(i, 0, 1000) p[i] = nullp;
rep(i, 1, n) cin >> p[i].x >> p[i].y;
if (n < 5) { cout << "Y\n"; continue; }
if (judge1()) cout << "Y\n";
else if (judge2()) cout << "Y\n";
else cout << "N\n";
}
}