HDU 3585 maximum shortest distance 二分+最大团

题意:n个点中找k个点使得它们最短距离最长。
思路:二分,二分这个距离。(如果一条边的长度>=这个距离,就可以连边。)然后看看最大团的大小,比k大还是比k小。

http://acm.hdu.edu.cn/showproblem.php?pid=3585

/*********************************************
    Problem : HDU 3585
    Author  : NMfloat
    InkTime (c) NM . All Rights Reserved .
********************************************/

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <string>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

#define rep(i,a,b)  for(int i = (a) ; i <= (b) ; i ++) //遍历
#define rrep(i,a,b) for(int i = (b) ; i >= (a) ; i --) //反向遍历
#define repS(it,p) for(auto it = p.begin() ; it != p.end() ; it ++) //遍历一个STL容器
#define repE(p,u) for(Edge * p = G[u].first ; p ; p = p -> next) //遍历u所连接的点
#define cls(a,x)   memset(a,x,sizeof(a))
#define eps 1e-8

using namespace std;

const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const int MAXN = 55;
const int MAXE = 2e5+5;

typedef long long LL;
typedef unsigned long long ULL;

int T,n,m;

int fx[] = {0,1,-1,0,0};
int fy[] = {0,0,0,-1,1};

struct Point {
    double x,y;
    double dist(const Point & B) const {
        return sqrt((x-B.x) * (x-B.x) + (y-B.y) * (y-B.y));
    } 
}A[MAXN];

double M[MAXN][MAXN];
int dp[MAXN];
int best;

bool dfs(int * id,int top,int cnt,double r) { 
    //id是和当前点有连接的数组,top是这个数组的长度,cnt已经找到的最大团的大小
    //id数组从1到n
    if(!top) { //dfs的结束标志
        if(best < cnt) {
            best = cnt ;
            return true; //代表这次搜索更新了结果
        }
        return false;
    }
    int a[MAXN];
    rep(i,1,top) {
        if(cnt+top-i+1<=best) return false; 
        //(top-1)-i+1是后继节点生效的个数 因为i是从0开始的 , top-i是在逐渐减少的,所有不用考虑后面的状态
        if(cnt+dp[id[i]]<=best) return false; 
        //cnt现在找到的最大团的个数 + dp[id[i]] , dp[id[i]] 是以id[i]开头最大的最大团个数
        //如果cnt+dp[id[i]]<=best就没有搜索的必要了 , 因为dp[id[i+k]] (k >= 1) 必然 <=dp[id[i]];
        int lan = 0;
        rep(j,i+1,top) if(M[id[i]][id[j]] >= r) a[++lan] = id[j]; 
        //有多少个点和id[i]这个点是连接 , a数组是不能预处理的,因为id[]是不确定的  
        if(dfs(a,lan,cnt+1,r)) return true;
        //因为每一次搜索最多只会让最大独立团的个数+1,所以如果更新过一次,就不用继续搜了。
    }
    return false;
}

int max_clique(double r) {
    int id[MAXN];
    best = 0;
    rrep(i,1,n) {
        int top = 0;
        rep(j,i+1,n) if(M[i][j] >= r) id[++top] = j;
        dfs(id,top,1,r);
        dp[i] = best;
    }
    return best;
}

int k;

void debug() {
    rep(i,1,n) {
        rep(j,1,n) printf("%f ",M[i][j]);
        puts("");
    }
}

void input() {
    rep(i,1,n) scanf("%lf %lf",&A[i].x,&A[i].y);
}

void solve() {
    rep(i,1,n) rep(j,1,n) M[i][j] = A[i].dist(A[j]);
    // debug();
    double l = 0 ; //二分长度
    double r = 20000;
    while(fabs(l-r)>eps) {
        double mid = (l + r) / 2;
        if(max_clique(mid) >= k) l = mid; 
        else r = mid;
    }
    printf("%.2f\n",l);
}

int main(void) {
    // freopen("a.in","r",stdin);
    while(~scanf("%d %d",&n,&k)) {
        input();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值