首页 > 代码库 > uva1084

uva1084

状压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;
}

 

uva1084