UVa 248 Cutting Corners <计算几何+SPFA>

避障最短路径算法
本文介绍了一种求解二维平面上避开矩形障碍物的最短路径算法。该算法通过构建图模型并利用起点、终点及障碍顶点作为节点进行路径规划。文章详细解释了如何判断线段交叉及点在矩形内的情况,并给出完整代码实现。

题目

题目大意:给定起点坐标和终点坐标,以及一些矩形区域的三个点坐标,求起点到终点的最短路径长度(路径不能穿过矩形矩形区域)。

分析:容易想到的是,将所有矩形的四个角以及起点和终点看做节点,若两个节点之间的连线不跨过任何矩形区域,则在此两节点间加一条边,边权即距离,如此建图后求最短路径就好了。需要注意的是,在矩形内部的节点应当删去,另外,因为我们判断是否跨过矩形是判断此线段与矩形的四条边是否规范相交,有可能此条线段是矩形的对角线,所以我们人为为每个矩形添加两条对角线,就可以解决这个问题了。

(这道题1A,开心~。。)

代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <bitset>
#include <queue>
#define V Point

using namespace std;

const double eps=1e-7;
const double INF=9999.0;
const  int MAXN=100;

struct Point;

struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y):x(_x),y(_y){}
    V operator -(Point p){
        return Point(x-p.x,y-p.y);
    }
    Point operator +(V v){
        return Point(x+v.x,y+v.y);
    }
};

double dis(V v){
    return sqrt(v.x*v.x+v.y*v.y);
}

int dblcmp(double x){
    return fabs(x)<eps?0:(x>0?1:-1);
}

double det(V v1,V v2){
    return v1.x*v2.y-v1.y*v2.x;
}

double dot(V v1,V v2){
    return v1.x*v2.x+v1.y*v2.y;
}

bool cross(Point a,Point b,Point c,Point d){
    return (dblcmp(det(b-a,c-a))^dblcmp(det(b-a,d-a)))==-2&&
            (dblcmp(det(d-c,a-c))^dblcmp(det(d-c,b-c)))==-2;
}

bool rightAngle(Point a,Point b,Point c){
    return 0==dblcmp(dot(b-a,c-a));
}

struct Segment{
    Point p1,p2;
    Segment(){}
    Segment(Point _p1,Point _p2):p1(_p1),p2(_p2){}
    bool onSegment(Point& p){
        return dblcmp(det(p1-p,p2-p))==0&&
                p.x>=min(p1.x,p2.x)&&p.x<=max(p1.x,p2.x)&&
                p.y>=min(p1.y,p2.y)&&p.y<=max(p1.y,p2.y);
    }
    bool intersection(Point a,Point b){
        return cross(p1,p2,a,b);
    }
};

struct House{
    Point p[4];
    Segment s[6];
    double area;
    House(){}
    House(Point *pp){
        for(int i=0;i<3;++i){
            if(rightAngle(pp[i],pp[(i+1)%3],pp[(i+2)%3])){
                p[0]=pp[(i+1)%3];
                p[1]=pp[i];
                p[2]=pp[(i+2)%3];
                p[3]=p[0]+(p[2]-p[1]);
                area=det(p[1]-p[0],p[3]-p[0]);
                break;
            }
        }
        for(int i=0;i<4;++i){
            s[i]=Segment(p[i],p[(i+1)%4]);
        }
        s[4]=Segment(p[0],p[2]);
        s[5]=Segment(p[1],p[3]);
    }
    bool inHouse(Point pp){
        bool flag=true;
        for(int i=0;i<4;++i){
            if(s[i].onSegment(pp)){
                flag=false;
                break;
            }
        }
        double areaTemp=0.0;
        for(int i=0;i<4;++i){
            areaTemp+=fabs(det(p[i]-pp,p[(i+1)%4]-pp));
        }
        return dblcmp(areaTemp/2.0-area)==0&&flag;
    }
};

House house[30];
Point point[MAXN];
int n,t;
double map[MAXN][MAXN];
double dist[MAXN];
int p_num;
Point start,endp;

void buildG(){
    p_num=0;
    point[p_num++]=start;
    for(int i=0;i<n;++i){
        for(int j=0;j<4;++j){
            int k;
            for(k=0;k<n;++k){
                if(i==k) continue;
                if(house[k].inHouse(house[i].p[j])) break;
            }
            if(k==n) point[p_num++]=house[i].p[j];
        }
    }
    point[p_num++]=endp;
    for(int i=0;i<p_num;++i){
        dist[i]=INF;
        for(int j=i+1;j<p_num;++j){
            map[i][j]=map[j][i]=INF;
            bool flag=true;
            for(int k=0;k<n&&flag;++k){
                for(int v=0;v<6;++v){
                    if(house[k].s[v].intersection(point[i],point[j])){   
                        flag=false;
                        break;
                    }
                }
            }
            if(flag) map[i][j]=map[j][i]=dis(point[i]-point[j]);
        }
    }
}

void solve(){
    bitset<MAXN> vis;
    queue<int> q;
    q.push(0);
    dist[0]=0;
    vis[0]=1;
    while(!q.empty()){
        int id=q.front();
        q.pop();
        vis[id]=0;
        for(int i=0;i<p_num;++i){
            if(dist[i]>dist[id]+map[id][i]){
                dist[i]=dist[id]+map[id][i];
                if(!vis[i]){
                    vis[i]=1;
                    q.push(i);
                }
            }
        }
    }
    if(t>1) putchar('\n');
    printf("Scenario #%d\n   route distance: %.2lf\n",t++,dist[p_num-1]);
}

int main(){
    t=1;
    while(scanf("%d",&n),n!=-1){
        double x1,x2,y1,y2;
        scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
        start=Point(x1,y1);
        endp=Point(x2,y2);
        Point pArray[3];
        for(int i=0;i<n;++i){
            for(int i=0;i<3;++i){
                scanf("%lf %lf",&x1,&y1);
                pArray[i]=Point(x1,y1);
            }
            house[i]=House(pArray);
        }
        buildG();
        solve();
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值