SGU128 Snake
题目大意
给出平面上N个点,要求用闭合无自交的折线连接起来
其中线段端点一定是给出的点,点要无重复无遗漏,相邻线段构成直角,线段要平行于坐标轴
找出总长度最短的折线
算法思路
由于线段端点必须构成直角,且不能自交
因此,每个点都必须要连出一条平行x轴的线段,和一条平行于y轴的线段
考虑y坐标等于某个值的所有点,最左边的点只能向右连,以此类推,整行的连线都是确定的
对于x坐标同样,因此,最终的连线方案是确定的,可以通过排序确定
判断是否无重复无遗漏、计算答案等用dfs可以轻易解决
而是否存在自交,需要使用扫描线进行判断
把每条横线的两端设为进入与离开事件,每条竖线是查询事件
按照x坐标排序后依次执行,插入、删除某个y坐标,查询一段区间内是否存在y坐标即可
可以使用set维护,注意x坐标相同时,事件的执行顺序(先删除再查询再插入)
时间复杂度: O(NlogN)
代码
/**
* Copyright © 2015 Authors. All rights reserved.
*
* FileName: 128.cpp
* Author: Beiyu Li <sysulby@gmail.com>
* Date: 2015-06-13
*/
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
typedef long long LL;
typedef pair<int, int> Pii;
const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 10000 + 5;
int n, sz;
int x[maxn], y[maxn], id[maxn];
struct Event {
int x, t, y, l, r;
Event() {}
Event(int x, int t, int y): x(x), t(t), y(y) {}
Event(int x, int t, int l, int r): x(x), t(t), l(l), r(r) {}
bool operator<(const Event &e) const
{ return x ^ e.x? x < e.x: t < e.t; }
} vec[maxn*2];
multiset<int> st;
multiset<int>::iterator it;
bool vis[maxn];
map<int, Pii> adj[maxn];
int dfs(int u, int d, int dep)
{
if (!adj[u].count(d)) return 0;
vis[u] = true;
int v = adj[u][d].first, w = adj[u][d].second;
if (vis[v]) return dep == n - 1? w: 0;
int ret = dfs(v, d ^ 1, dep + 1);
return ret? ret + w: 0;
}
int cmpx(int a, int b) { return x[a] ^ x[b]? x[a] < x[b]: y[a] < y[b]; }
int cmpy(int a, int b) { return y[a] ^ y[b]? y[a] < y[b]: x[a] < x[b]; }
int solve()
{
rep(i,n) id[i] = i;
sort(id, id + n, cmpy);
for (int i = 0, j; i < n; i = j) {
for (j = i + 1; j < n && y[id[j]] == y[id[i]]; ++j);
if ((j - i) & 1) return 0;
for (int k = i; k < j; k += 2) {
int u = id[k], v = id[k+1];
adj[u][0] = Pii(v, x[v] - x[u]);
adj[v][0] = Pii(u, x[v] - x[u]);
vec[sz++] = Event(x[u], 1, y[u]);
vec[sz++] = Event(x[v], -1, y[v]);
}
}
rep(i,n) id[i] = i;
sort(id, id + n, cmpx);
for (int i = 0, j; i < n; i = j) {
for (j = i + 1; j < n && x[id[j]] == x[id[i]]; ++j);
if ((j - i) & 1) return 0;
for (int k = i; k < j; k += 2) {
int u = id[k], v = id[k+1];
adj[u][1] = Pii(v, y[v] - y[u]);
adj[v][1] = Pii(u, y[v] - y[u]);
vec[sz++] = Event(x[u], 0, y[u], y[v]);
}
}
sort(vec, vec + sz);
rep(i,sz) {
if (vec[i].t < 0) st.erase(st.find(vec[i].y));
if (vec[i].t > 0) st.insert(vec[i].y);
if (!vec[i].t) {
it = st.upper_bound(vec[i].l);
if (it != st.end() && *it < vec[i].r) return 0;
}
}
return dfs(0, 0, 0);
}
int main()
{
scanf("%d", &n);
rep(i,n) scanf("%d%d", &x[i], &y[i]);
printf("%d\n", solve());
return 0;
}