bzoj 2304: [Apio2011]寻路 (最短路+建图)

本文介绍了一种解决在多个矩形障碍物环境中寻找从起点到终点最短路径的方法。通过按坐标排序并连接可达点来建立图,再利用SPFA算法求解最短路径。

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

题目描述

传送门

题解

对于矩形的每个顶点找到能到达的其他矩形边界上的点,然后将所有有用的点找出来。
先按照纵坐标排序,然后将纵坐标相同的点中相邻的点连边,注意在边界上的点需要特判,因为不能进入矩形。
然后按照横坐标排序,处理方式与纵坐标相同。
注意需要特判一下起点与终点的位置,因为有可能在矩形的边界上。
连完边,跑最短路即可。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#define inf 1000000000
#define N 2000003
#define M 3020
#define LL long long 
using namespace std;
struct data{
    int x1,x2,y1,y2;
}a[M];
struct node{
    int x,y,opt,pd,id;
}p[M*20];
int n,m,sx,sy,tx,ty,lx[M],ly[M],cntx,cnty,cnt;
int tot,point[N],nxt[N],v[N],can[N],T,st[N];
LL dis[N],c[N];
void add(int x,int y,LL z)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
}
LL spfa(int s,int t)
{
    queue<int> p;
    memset(dis,127/3,sizeof(dis));
    for (int i=1;i<=cnt;i++) can[i]=0; 
    p.push(s); can[s]=1; dis[s]=0;
    while (!p.empty()){
        int now=p.front(); p.pop();
        for (int i=point[now];i;i=nxt[i])
         if (dis[v[i]]>dis[now]+c[i]) {
            dis[v[i]]=dis[now]+c[i];
            if (!can[v[i]]) {
                can[v[i]]=1;
                p.push(v[i]);
             }
         }
        can[now]=0;
    } 
    return dis[t];
}
void findup(int now,int x,int y)
{
    int mx=0; int pos=0;
    for (int i=1;i<=n;i++) {
        if (i==now) continue;
        if(a[i].y1<=y&&a[i].y2>=y&&a[i].x2<x) 
          if (mx<a[i].x2) mx=max(mx,a[i].x2),pos=i;
    }
    if (a[pos].y1==y||a[pos].y2==y||mx==0) return;
    p[++cnt].x=mx; p[cnt].y=y; p[cnt].opt=1;
}
void finddown(int now,int x,int y)
{
    int mn=inf; int pos=0;
    for (int i=1;i<=n;i++) {
        if (i==now) continue;
        if(a[i].y1<=y&&a[i].y2>=y&&a[i].x1>x) 
          if (mn>a[i].x1) mn=min(mn,a[i].x1),pos=i;
    }
    if (a[pos].y1==y||a[pos].y2==y||mn==inf) return;
    p[++cnt].x=mn; p[cnt].y=y; p[cnt].opt=2;
}
void findl(int now,int x,int y)
{
    int mx=0; int pos=0;
    for (int i=1;i<=n;i++) {
        if (i==now) continue;
        if (a[i].x1<=x&&x<=a[i].x2&&a[i].y2<y) 
         if (mx<a[i].y2) mx=a[i].y2,pos=i;
    }
    if (a[pos].x1==x||a[pos].x2==x||mx==0) return;
    p[++cnt].x=x; p[cnt].y=mx; p[cnt].opt=3;
}
void findr(int now,int x,int y)
{
    int mn=inf; int pos=0;
    for (int i=1;i<=n;i++) {
        if (i==now) continue;
        if (a[i].x1<=x&&x<=a[i].x2&&a[i].y1>y) 
         if (mn>a[i].y1) mn=a[i].y1,pos=i;
    }
    if (a[pos].x1==x||a[pos].x2==x||mn==inf) return;
    p[++cnt].x=x; p[cnt].y=mn; p[cnt].opt=4;
}
int check(int x,int y,int x1,int y1,int x2,int y2)
{
    if (x==x1&&y>y1&&y<y2) return 2;
    if (x==x2&&y>y1&&y<y2) return 1;
    if (y==y1&&x>x1&&x<x2) return 4;
    if (y==y2&&x>x1&&x<x2) return 3;
    return 0;
}
int cmp(node a,node b){
    return a.x<b.x||a.x==b.x&&a.y<b.y;
}
int cmp1(node a,node b){
    return a.y<b.y||a.y==b.y&&a.x<b.x;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d",&T);
    while (T--) {
        scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
        cnt=0; tot=0; cntx=cnty=0;
        lx[++cntx]=sx; lx[++cntx]=tx; 
        ly[++cnty]=sy; ly[++cnty]=ty;
        memset(point,0,sizeof(point));
        memset(p,0,sizeof(p));
        scanf("%d",&n);
        for (int i=1;i<=n;i++){
            scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
            if (a[i].x1>a[i].x2) swap(a[i].x1,a[i].x2);
            if (a[i].y1>a[i].y2) swap(a[i].y1,a[i].y2);
            lx[++cntx]=a[i].x1; lx[++cntx]=a[i].x2; 
            ly[++cnty]=a[i].y1; ly[++cnty]=a[i].y2;
        }
        sort(lx+1,lx+cntx+1); cntx=unique(lx+1,lx+cntx+1)-lx-1;
        sort(ly+1,ly+cnty+1); cnty=unique(ly+1,ly+cnty+1)-ly-1;
        sx=lower_bound(lx+1,lx+cntx+1,sx)-lx;
        sy=lower_bound(ly+1,ly+cnty+1,sy)-ly;
        tx=lower_bound(lx+1,lx+cntx+1,tx)-lx;
        ty=lower_bound(ly+1,ly+cnty+1,ty)-ly;
        ++cnt; p[cnt].x=sx; p[cnt].y=sy; p[cnt].pd=1;
        ++cnt; p[cnt].x=tx; p[cnt].y=ty; p[cnt].pd=2;
        for (int i=1;i<=n;i++) {
            a[i].x1=lower_bound(lx+1,lx+cntx+1,a[i].x1)-lx;
            a[i].y1=lower_bound(ly+1,ly+cnty+1,a[i].y1)-ly;
            a[i].x2=lower_bound(lx+1,lx+cntx+1,a[i].x2)-lx;
            a[i].y2=lower_bound(ly+1,ly+cnty+1,a[i].y2)-ly;
            ++cnt; p[cnt].x=a[i].x1; p[cnt].y=a[i].y1;
            ++cnt; p[cnt].x=a[i].x1; p[cnt].y=a[i].y2;
            ++cnt; p[cnt].x=a[i].x2; p[cnt].y=a[i].y1;
            ++cnt; p[cnt].x=a[i].x2; p[cnt].y=a[i].y2;
            p[1].opt=max(p[1].opt,check(sx,sy,a[i].x1,a[i].y1,a[i].x2,a[i].y2));
            p[2].opt=max(p[2].opt,check(tx,ty,a[i].x1,a[i].y1,a[i].x2,a[i].y2));
        }
        a[++n].x1=a[n].x2=tx; a[n].y1=a[n].y2=ty;
        for (int i=1;i<=n-1;i++) {
            findup(i,a[i].x1,a[i].y1);  findup(i,a[i].x1,a[i].y2);
            finddown(i,a[i].x2,a[i].y1); finddown(i,a[i].x2,a[i].y2);
            findl(i,a[i].x1,a[i].y1); findl(i,a[i].x2,a[i].y1);
            findr(i,a[i].x1,a[i].y2); 
            findr(i,a[i].x2,a[i].y2); 
        }
        if(p[1].opt!=1)findup(0,sx,sy); 
        if(p[1].opt!=2)finddown(0,sx,sy);
        if(p[1].opt!=3)findl(0,sx,sy); 
        if(p[1].opt!=4)findr(0,sx,sy);
        if(p[2].opt!=1)findup(n,tx,ty);
        if(p[2].opt!=2)finddown(n,tx,ty);
        if(p[2].opt!=3)findl(n,tx,ty);
        if(p[2].opt!=4)findr(n,tx,ty);
        for (int i=1;i<=cnt;i++) p[i].id=i;
        sort(p+1,p+cnt+1,cmp); int j=1;
        for (int i=1;i<=cntx;i++) {
            int top=0;
            while (p[j].x==i&&j<=cnt) {
                st[++top]=j;
                j++;
            }
            if (top<=1) continue;
            if (p[st[1]].opt!=4&&p[st[1]].pd!=2) add(p[st[1]].id,p[st[2]].id,ly[p[st[2]].y]-ly[p[st[1]].y]);
            if (p[st[top]].opt!=3&&p[st[top]].pd!=2) add(p[st[top]].id,p[st[top-1]].id,ly[p[st[top]].y]-ly[p[st[top-1]].y]);
            for (int j=2;j<=top-1;j++) {
                if (p[st[j]].opt!=4&&p[st[j]].pd!=2) add(p[st[j]].id,p[st[j+1]].id,ly[p[st[j+1]].y]-ly[p[st[j]].y]);
                if (p[st[j]].opt!=3&&p[st[j]].pd!=2)  add(p[st[j]].id,p[st[j-1]].id,ly[p[st[j]].y]-ly[p[st[j-1]].y]);
            }
        } 
        j=1; int S,T;
        sort(p+1,p+cnt+1,cmp1);
        for (int i=1;i<=cnty;i++) {
            int top=0;
            while (p[j].y==i&&j<=cnt) {
                st[++top]=j;
                if (p[j].pd==1) S=p[j].id;
                if (p[j].pd==2) T=p[j].id;
                j++;
            }
            if (top<=1) continue;
            if (p[st[1]].opt!=2&&p[st[1]].pd!=2) add(p[st[1]].id,p[st[2]].id,lx[p[st[2]].x]-lx[p[st[1]].x]);
            if (p[st[top]].opt!=1&&p[st[top]].pd!=2) add(p[st[top]].id,p[st[top-1]].id,lx[p[st[top]].x]-lx[p[st[top-1]].x]);
            for (int j=2;j<=top-1;j++) {
                if (p[st[j]].opt!=2&&p[st[j]].pd!=2) add(p[st[j]].id,p[st[j+1]].id,lx[p[st[j+1]].x]-lx[p[st[j]].x]);
                if (p[st[j]].opt!=1&&p[st[j]].pd!=2)  add(p[st[j]].id,p[st[j-1]].id,lx[p[st[j]].x]-lx[p[st[j-1]].x]);
            }
        }
        LL ans=spfa(S,T); 
        if (ans==dis[0]) printf("No Path\n");
        else printf("%lld\n",spfa(S,T));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值