POJ 3714 Raid

//POJ 3714 Raid
//AC by warteac
//2013-7-19
/*
    法一:先排序,然后依次向后找最小距离,并且剪枝
         重点是,不需要指定找 人到站点或是站点到人的距离,把所有的都看成点,就找两点最短距离即可
         为了区别用flag标记,求距离时判断flag是否相同。
    6764K 3579MS C++ 2090B 2013-07-19 11:44:37 
    法二:二分是高效算法;
         重点是,合并的时候,求最短距离横跨左右两边的情况,有一个剪枝的动作,不加依然会超时,详见代码
         TLE: 重复调用函数导致超时,不是算法的问题,详见代码
    6768K 2922MS C++ 2725B 2013-07-19 11:46:29 
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include <iomanip> 
#include<algorithm>
using namespace std;

const double eps = 1e-5;
const double MAXLEN = 1000000000.0;
struct posi{
    double x;
    double y;
    bool flag;   //0 is agent,1 is raid
};

vector <posi> p;
int num;
double min_length;

double length(posi p1, posi p2){
    if(p1.flag != p2.flag)
    return sqrt((p2.x - p1.x)*(p2.x - p1.x)+(p2.y - p1.y)*(p2.y - p1.y));
    else return MAXLEN;
}
bool cmp(posi p1, posi p2){
    if(fabs(p1.x - p2.x) < eps){
        return p1.y < p2.y;
    }else{
        return p1.x < p2.x;
    }
}
//法一/
/*double getMinLength(int i){
    double min = MAXLEN;
    for(int j = i+1; j < 2*num; j++){
        if(p[i].flag != p[j].flag){
             double temp = length(p[i],p[j]);
        if(temp - min < eps){
            min = temp;
        }
        else if(fabs(p[i].x - p[j].x) > min)//如果不加这句if(),有BUG,但POJ没有检查出来,具体测试数据见下面
                break;
        }
    }
    return min;
}*/
法一结束/

法二
double getMinLength(int x, int y){
    if(y - x <= 2){
        double len,minlen = MAXLEN;
        for(int i = x; i < y; i++)
            for(int j = x+1; j < y; j++){
                len = length(p[i],p[j]);
                //minlen <?= len;
                if(len < minlen) minlen = len;
            }
    return minlen;
    }else{
        double r,result = MAXLEN;
        int i,j;
        int m = (x + y) / 2;//第一步:划分
        //result = getMinLength(x,m) < getMinLength(m,y) ? getMinLength(x,m) : getMinLength(m,y);//第二步:递归解决子问题
        //TLE: 上式中调用getMinLength()四次,在递归次数庞大时就会导致超时了
        //解决办法,用两个变量Llen,Rlen 保存函数的调用值
        double Llen,Rlen;
        Llen = getMinLength(x,m);
        Rlen = getMinLength(m,y);
        result = Llen < Rlen ? Llen : Rlen;//第二步:递归解决子问题
        for(i = m-1; i >= x; i--){//第三步:合并
            if(fabs(p[m].x - p[i].x) > result) break;//剪枝
            for(j = m; j < y; j++){
                if(fabs(p[m].x - p[j].x) > result) break;//剪枝
                r = length(p[i],p[j]);
                //result <?= r;
                if(result > r) result = r;
            }
        } 
       return result;
    }
}
/法二结束///

int main(){
    int n,i,j;
    cin >> n;
    while(n--){
        posi t;
        cin >> num;
        p.clear();
        min_length = MAXLEN;
        for(i = 0; i < num; i++){
            cin >> t.x >> t.y;
            t.flag = 0;//agent
            p.push_back(t);
        }
        for(i = 0; i < num; i++){
            cin >> t.x >> t.y;
            t.flag = 1;//raid
            p.push_back(t);
        }
        sort(p.begin(),p.end(),cmp);
        
        //法一///
        /* for(int k = 0 ;k < p.size();k++){
        }
        for(j = 0; j < 2*num-1; j++){
            double mlen = getMinLength(j);
            if(mlen < min_length) min_length = mlen;
        }*/
        //法一结束/
        
        //法二//
        min_length = getMinLength(0,p.size());
        //法二结束//
        
        cout <<setiosflags(ios::fixed);  
        cout << setprecision(3);
        cout << min_length << endl;
    }
    return 0;
}

/*
测试BUG的数据
INPUT:
1
3
0 0
13 13
15 15
2 5
4 0
4 -6
OUTPUT:(right)
4.000
OUTPUT:(wrong)
5.385    
*/


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值