题意:从n个点中任取k个点,求这k个点两两之间最小距离的最大值。
解析:先把n个点之间的不同距离保存下来,从小到大排序后二分距离,比该距离小的边当作不存在,然后把剩余的边求最大团。如果最大团顶点数大于等于k,就向上缩小区间,否则向下缩小区间。(首先可以肯定的是该问题一定有解,就是二分一定能得到一个最佳距离。等于k的时候还需要向上缩小区间是为了得到最大值。)
最大团采用的是BK算法
朴素版本:
BronKerbosch(All, Some, None):
if Some and None are both empty:
report All as a maximal clique
for each vertex v in Some:
BronKerbosch1(All ⋃ {v}, Some ⋂ N(v), None ⋂ N(v))
Some := Some \ {v}
None := None ⋃ {v}
优化版本:
BronKerbosch(All, Some, None):
if Some and None are both empty:
report All as a maximal clique
choose a pivot vertex u in Some ⋃ None
for each vertex v in Some \ N(u)::
BronKerbosch1(All ⋃ {v}, Some ⋂ N(v), None ⋂ N(v))
Some := Some \ {v}
None := None ⋃ {v}
all是当前已经被选的顶点集,some是客观上可以选主观上也打算选的顶点集,none是客观上可以选主观上不打算选的顶点集。n(v)是v的邻接点。
只有当some和none都为空的时候才是一个极大团,这时候保证some中没有顶点可以再加入all,并且none中如果有点,那么它客观上其实也是能加入到all的,所以none必须也为空。
每一个在some中的点v,都有加入all和不加入all两种选择,如果加入all,那么除了all改变,some和none由于下一次只能取和v邻接的点,取了n(v)的交集。
接下来就是不选的情况。
优化版本可以避免把some中所有的顶点全部循环一遍,任意一个点u,如果最大团中既不包括u又不包括u的非邻接点,那么一定可以把u加入到最大团中,这样就和假设矛盾,所以一定在两者之间包括一个,不可能都包括。在后面的模板中,让u从some中取,让u等于some中的第一个点,i=0的时候就是取u的情况,i>0的时候是取u的非邻接点的情况。当然如果u从none中取也可以,只要改成一开始u=none[d][0]。只能取u的非邻接点,u从none中来所以不能给all。但是这样也起到剪枝的效果。
还有一个问题就是,有没有可能all里面就有u的非邻接点呢?不可能,因为如果有,那么some里面不可能出现u了。
void dfs(int d, int an, int sn, int nn)
{
if(sn == 0 && nn == 0) ++ S; //得到的是极大团,最大团是极大团里面顶点数最多的
int u = some[d][0];//pivot vertex
for(int i = 0; i < sn; i ++)
{
int v = some[d][i];
if(g[u][v]) continue;
int tsn = 0, tnn = 0;
for(int j = 0; j < an; j ++) all[d + 1][j] = all[d][j];
all[d + 1][an] = v;
for(int j = 0; j < sn; j ++)if(g[v][some[d][j]]) some[d + 1][tsn ++] = some[d][j];
for(int j = 0; j < nn; j ++) if(g[v][none[d][j]]) none[d + 1][tnn ++] = none[d][j];
dfs(d + 1, an + 1, tsn, tnn);
//把v从some取出,放入none
some[d][i] = 0, none[d][nn ++] = v;
}
}
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 500
using namespace std;
int n, k, cnt, l, r, mid, le;
double ans;
int x[N], y[N], d[N][N], dist[1000000], a[N], some[N][N], all[N][N], none[N][N];
int getdist(int u, int v)
{
return (x[u] - x[v]) * (x[u] - x[v]) + (y[u] - y[v]) * (y[u] - y[v]);
}
int dfs(int deep, int an, int sn, int nn)
{
if (an >= k) return 1;
if (an + sn < k) return 0;
int u = some[deep][1];
for (int i = 1; i <= sn; i++)
{
int v = some[deep][i];
int tsn = 0, tnn = 0;
if (d[u][v] >= le) continue;
for (int j = 1; j <= an; j++) all[deep+1][j] = all[deep][j];
all[deep+1][an+1] = v;
for (int j = 1; j <= sn; j++)
if (d[v][some[deep][j]] >= le) some[deep+1][++tsn] = some[deep][j];
for (int j = 1; j <= nn; j++)
if (d[v][none[deep][j]] >= le) none[deep+1][++tnn] = none[deep][j];
if (dfs(deep+1, an+1, tsn, tnn)) return 1; // 这里要return 1;
some[deep][i] = 0;
none[deep][++nn] = v;
}
return 0;
}
int main()
{
while(~scanf("%d%d", &n, &k))
{
for (int i = 1; i <= n; i++)
scanf("%d%d", &x[i], &y[i]);
cnt = 0;
memset(d, 0, sizeof(d));
for (int i = 1; i < n; i++)
for (int j = i+1; j <= n; j++)
{
d[i][j] = getdist(i, j);
d[j][i] = d[i][j];
cnt++;
dist[cnt] = d[i][j];
}
sort(dist+1, dist+cnt+1);
l = 1;
r = cnt;
while (l <= r)
{
mid = (l + r) / 2;
le = dist[mid];
a[0] = 0;
for (int i = 1; i <= n; i++) some[1][i] = i;
if (dfs(1, 0, n, 0))
{
l = mid + 1;
ans = sqrt(dist[mid]);
}
else
{
r = mid - 1;
}
}
printf("%.2f\n", ans);
}
return 0;
}