HDU - 3585 maximum shortest distance(二分+最大团)

本文介绍了一种利用最大团算法解决选取平面上K个点,使其中任意两点间最小距离最大化的问题。通过二分查找和Bron-Kerbosch算法高效找到解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

maximum shortest distance

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2385    Accepted Submission(s): 783


Problem Description
There are n points in the plane. Your task is to pick k points (k>=2), and make the closest points in these k points as far as possible.
 

Input
For each case, the first line contains two integers n and k. The following n lines represent n points. Each contains two integers x and y. 2<=n<=50, 2<=k<=n, 0<=x,y<10000.
 

Output
For each case, output a line contains a real number with precision up to two decimal places.

 

Sample Input

 
3 2 0 0 10 0 0 20
 

Sample Output

 
22.36
 

Author
alpc50
 

Source
 

Recommend
zhouzeyong
 


题意:从平面中选K个点,使得这k个点中最近的两个点的距离尽可能的大。


解题思路:实在没想到竟然可以用最大团去做!太巧了!首先把所有点两两间的距离算出来,很容易想到答案就在这些距离中。所以我们可以二分答案,对于当前答案,把比这个长的边全部连起来生成图,如果该图的最大团比k大,那么成立。


这里坑爹,不能用朴素的最大团求法,不然会超时,要用Bron-Kerbosch算法求最大团,中间很好剪枝,速度极快。


#include <iostream>
#include <vector>
#include <map>
#include <math.h>
#include <algorithm>
#include <memory.h>
using namespace std;
struct point
{
    double x;
    double y;
    int index;
    point(int a, int b, int c)
    {
        x = a;
        y = b;
        index = c;
    }
};

int n, k;
int G[55][55];
double dis[55][55];
int all[200][200]; //第j个极大团所包含的节点
int some[200][200];
int none[200][200];

vector<point> ps;
vector<double> ans;
//all集合记录的是当前极大团中已经加入的点
//some集合记录的是可能还能加入的点(也就是说可能与R集合中所有点都有边存在的点)
//none集合记录的是已经完成极大团计数的点(作用是判重)
bool dfs(int x, int an, int sn, int nn)
{
    if (an >= k)//搜索到了答案
        return 1;

    if (an + sn < k)//剪枝
        return 0;

    int key = some[x][1];
    for (int j = 1; j <= sn; j++)
    {
        int v = some[x][j];
        int tsn = 0;
        int tnn = 0;

        //剪枝,与key相连的,必被搜索过
        if (G[key][v])
            continue;

        for (int i = 1; i <= an; i++)
            all[x + 1][i] = all[x][i];
        all[x + 1][an + 1] = v;

        for (int i = 1; i <= sn; i++)
            if (G[v][some[x][i]])
                some[x + 1][++tsn] = some[x][i];

        for (int i = 1; i <= nn; i++)
            if (G[v][none[x][i]])
                none[x + 1][++tnn] = none[x][i];

        if (dfs(x + 1, an + 1, tsn, tnn))
            return 1;

        some[x][j] = 0;
        none[x][++nn] = v;
    }
    return 0;
}

bool judge(int m)
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (i != j)
                if (dis[i][j] >= ans[m])
                {
                    G[i][j] = 1;
                }
                else
                {
                    G[i][j] = 0;
                }
        }
    }
    for (int i = 1; i <= n; i++)
        some[1][i] = i;

    if (dfs(1, 0, n, 0))
        return 1;
    else
        return false;
}

int main()
{

    int a, b;
    while (~scanf("%d%d", &n, &k))
    {
        ps.clear();
        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d", &a, &b);
            ps.push_back(point(a, b, i));
        }

        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (i != j)
                {
                    dis[i][j] = sqrt((ps[i - 1].x - ps[j - 1].x) * (ps[i - 1].x - ps[j - 1].x) + (ps[i - 1].y - ps[j - 1].y) * (ps[i - 1].y - ps[j - 1].y));
                    ans.push_back(dis[i][j]);
                    //cout<<sqrt((ps[i - 1].x - ps[j - 1].x) * (ps[i - 1].x - ps[j - 1].x) + (ps[i - 1].y - ps[j - 1].y) * (ps[i - 1].y - ps[j - 1].y))<<endl;
                }
            }
        }

        sort(ans.begin(), ans.end());

        int l = 0, r = unique(ans.begin(), ans.end()) - ans.begin();

        while (l < r)
        {
            int m = (l + r) / 2;
            if (judge(m))
            {
                l = m + 1;
            }
            else
                r = m;
        }

        //cout<<l<<endl;
        printf("%.2lf\n", ans[l - 1]);
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值