状压dp+凸包
并没有看出来凸包的性质
首先答案一定在凸包上,然后每个凸包的角加起来是一个圆,那么就相当于凸包周长加一个圆了。然后预处理,再状压dp计算即可。
#include<bits/stdc++.h> using namespace std; const int N = 10; const double pi = acos(-1); struct points { double x, y; bool friend operator < (points A, points B) { return A.x == B.x ? A.y < B.y : A.x < B.x; } } point[N * 10], p[N * 10]; int n, m, cnt, top, kase; double dp[1 << N], d[1 << N]; int st[N * 10]; double cross(points x, points y, points z) { return (x.x - z.x) * (y.y - z.y) - (x.y - z.y) * (y.x - z.x); } double dis(points x, points y) { return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y)); } double graham() { double ret = 0; top = 0; for(int i = 1; i <= cnt; ++i) { while(top > 1 && cross(p[i], p[st[top]], p[st[top - 1]]) >= 0) --top; st[++top] = i; } int lim = top; for(int i = cnt - 1; i; --i) { while(top > lim && cross(p[i], p[st[top]], p[st[top - 1]]) >= 0) --top; st[++top] = i; } for(int i = 1; i < top; ++i) ret += dis(p[st[i]], p[st[i + 1]]); return ret; } int main() { while(scanf("%d%d", &n, &m)) { if(!n && !m) break; for(int i = 0; i < n; ++i) scanf("%lf%lf", &point[i].x, &point[i].y); sort(point, point + n); for(int i = 1; i < (1 << n); ++i) { cnt = 0; for(int j = 0; j < n; ++j) if(i & (1 << j)) p[++cnt] = point[j]; dp[i] = graham() + 2 * m * pi; } for(int i = 1; i < (1 << n); ++i) for(int S = i; S; S = (S - 1) & i) dp[i] = min(dp[i], dp[S] + dp[i ^ S]); printf("Case %d: length = %.2f\n", ++kase, dp[(1 << n) - 1]); } return 0; }