此题的意思就是,Wodex和他的一个伙伴要经过所有的圆盘(点),他们所走的距离要在M内,所以肯定要求最短路。
起先他们可以各自选择一个点作为起始点,然后开始走。
然后走的方式是这样的,走过最外面一圈的任意一点的那个人就不能走除最外面那些点以外的点(我们称这些点为内点好了)了,而且Wodex的伙伴因为能力不足无法进入内部的点,所以,两个人必须分工完成,伙伴走外面一圈,Wodex走内点。
为求走的路最短,那么外层就是一个凸包,凸包的周长减去凸包上某相邻两点的最大的那个距离就可以使距离最短了。
然后是里面的点,因为Wodex会分身术,制造很多分身(分身也可以使用分身术)帮他来走分支的路线,那么很显然,就是一个最小生成树。
最后凸包那边求得的距离与树的值相加与M比较一下就可以得到结论了。
需要注意的是,当所有点都是外点时,此时没有内点,那么两个人都要在外点上走,那么答案就是凸包的周长减去最大的距离和次大的距离了。
当点数只有1或者2时,那么所走的距离就是0,输出一定是YES了。
出题代码:
#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <cmath>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
double m;
struct pt
{
double x, y;
bool used;
};
pt save[110];
int s[110];
double td[110];
double sm[10010][3];
int v[110];
int r[110];
bool mult(pt a, pt b, pt c)
{
if ((a.x - c.x) * (b.y - c.y) >= (b.x - c.x) * (a.y - c.y)) return 1;
else return 0;
}
int cmp(const void *p, const void *q)
{
if ((*(pt *)p).used != (*(pt *)q).used) return (*(pt *)p).used - (*(pt *)q).used;
else if ((*(pt *)p).x != (*(pt *)q).x) return (*(pt *)p).x - (*(pt *)q).x;
else return (*(pt *)p).y - (*(pt *)q).y;
}
int cmp2(const void *x, const void *y)
{
return *(double *)x > *(double *)y ? 1 : -1;
}
double dis(pt a, pt b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int Andrew()
{
qsort(save, n, sizeof(save[0]), cmp);
s[0] = 0;
s[1] = 1;
int top = 1;
for (int i = 2; i < n; i++)
{
while (top && mult(save[i], save[s[top]], save[s[top - 1]])) top--;
s[++top] = i;
}
int len = top;
s[++top] = n - 2;
for (int i = n - 3; i >= 0; i--)
{
while (top != len && mult(save[i], save[s[top]], save[s[top - 1]])) top--;
s[++top] = i;
}
return top;
}
int find(int x)
{
if (v[x] != x) v[x] = find(v[x]);
return v[x];
}
void Union(int x, int y)
{
if (r[x] > r[y]) v[y] = x;
else if (r[x] < r[y]) v[x] = y;
else
{
v[x] = y;
r[y]++;
}
}
int main()
{
//freopen("data.in", "r", stdin);
//freopen("data.out", "w", stdout);
while (scanf("%d%lf", &n, &m) && (m || n))
{
memset(save, 0, sizeof(save));
for (int i = 0; i < n; i++)
{
scanf("%lf%lf", &(save[i].x), &(save[i].y));
save[i].used = 0;
}
if (n <= 2)
{
printf("0.0000\n");
continue;
}
double d = 0;
int t = Andrew();
for (int i = 0; i < t; i++)
{
save[s[i]].used = 1;
td[i] = dis(save[s[i]], save[s[(i + 1) % t]]);
d += td[i];
}
sort(td, td + t);
int l = n - t;
if (l == 0) d -= (td[t - 1] + td[t - 2]);
else
{
d -= td[t - 1];
if (l != 1)
{
int num = 0;
qsort(save, n, sizeof(save[0]), cmp);
for (int i = 0; i < l; i++)
{
for (int j = i + 1; j < l; j++)
{
sm[num][0] = dis(save[i], save[j]);
sm[num][1] = i;
sm[num][2] = j;
num++;
}
}
qsort(sm, num, sizeof(sm[0]), cmp2);
memset(r, 0, sizeof(r));
for (int i = 0; i < l; i++) v[i] = i;
int kk = 1;
for (int i = 0; i < num; i++)
{
int s1 = find((int)sm[i][1]);
int s2 = find((int)sm[i][2]);
if (s1 != s2)
{
kk++;
d += sm[i][0];
Union(s1, s2);
}
if (kk == l) break;
}
}
}
if (d > m) printf("NO\n");
else printf("%.4lf\n", d);
}
return 0;
}