[BZOJ2304][Apio2011]寻路(模拟+spfa)

题目描述

传送门

题目大意:在二维平面上有起点和终点,若干不相交、边界平行或竖直的矩形。不能进入矩形内部,只能在矩形的边界上改变方向,求起点到终点的最短距离或者No Path。

题解

这题和之前写过的冰原探险那道题挺像的,都是将这种图连边然后跑最短路
因为最优情况下只会在矩形的顶点处改变方向,所以可以先将坐标离散化,然后对于矩形的每一个顶点向第一个能到达的地方连边
这样的话除了矩形的顶点图上还会多出来一些有效的点,对于所有的有效点向其四个方向最近的点连边,然后跑最短路就行了
刚开始的时候计算上限的时候算错了。。刚开始以为有点数和边数都是 O(n2) 级别的,因为两两枚举。实际上这样计算是错的,因为从一个矩形出发至多只会搞出来12个新的有效点,并且每个点都至多会连出去4条边,所以点数和边数实际上都是 O(n) 级别的,这样就没问题了
注意特判:1、起点和终点在矩形里的情况2、矩形互相包含的情况(虽然ATP贴心的数据生成器都把这些恶心情况判掉了

代码

#include
#include
#include
#include
#include
#include
using namespace std;

int T,n,xs,ys,xt,yt,xx,yy,s,t,cnt;long long inf;
int a[1005],b[1005],c[1005],d[1005],X[2005],Y[2005],squ[2005][2005],pt[2005][2005];
int tot,point[24005],nxt[200005],v[200005];long long l[200005];
long long dis[24005];bool vis[24005],flag[2005];
struct data{int x,y,id;}p[24005];
queue <int> q;

void clear()
{
    n=xs=ys=xt=yt=xx=yy=s=t=cnt=tot=0;inf=0;
    memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(c,0,sizeof(c));memset(d,0,sizeof(d));
    memset(X,0,sizeof(X));memset(Y,0,sizeof(Y));memset(squ,0,sizeof(squ));memset(pt,0,sizeof(pt));
    memset(point,0,sizeof(point));memset(nxt,0,sizeof(nxt));memset(v,0,sizeof(v));
    memset(l,0,sizeof(l));memset(dis,0,sizeof(dis));memset(vis,0,sizeof(vis));
}
void walk(int x,int y,int dx,int dy)
{
    x+=dx,y+=dy;
    while (!squ[x][y])
    {
        if (x<1||x>xx||y<1||y>yy) return;
        x+=dx,y+=dy;
    }
    pt[x][y]=1;
}
int cmpx(data a,data b){return a.xint cmpy(data a,data b){return a.ylong long Abs(long long x){return (x>0)?x:-x;}
long long Dis(data a,data b){return Abs(X[a.x]-X[b.x])+Abs(Y[a.y]-Y[b.y]);}
void add(int x,int y,long long z)
{
    if (x==y) return;
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; l[tot]=z;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; l[tot]=z;
}
void spfa()
{
    memset(dis,127,sizeof(dis));inf=dis[0];
    dis[s]=0;vis[s]=1;q.push(s);
    while (!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for (int i=point[now];i;i=nxt[i])
            if (dis[v[i]]>dis[now]+l[i])
            {
                dis[v[i]]=dis[now]+l[i];
                if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
            }
    }
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        clear();
        scanf("%d%d%d%d",&xs,&ys,&xt,&yt);
        X[++xx]=xs,X[++xx]=xt,Y[++yy]=ys,Y[++yy]=yt;
        scanf("%d",&n);
        for (int i=1;i<=n;++i)
        {
            scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
            if (a[i]>c[i]) swap(a[i],c[i]);
            if (b[i]>d[i]) swap(b[i],d[i]);
            X[++xx]=a[i],X[++xx]=c[i],Y[++yy]=b[i],Y[++yy]=d[i];
        }
        sort(X+1,X+xx+1);xx=unique(X+1,X+xx+1)-X-1;
        sort(Y+1,Y+yy+1);yy=unique(Y+1,Y+yy+1)-Y-1;
        xs=lower_bound(X+1,X+xx+1,xs)-X;xt=lower_bound(X+1,X+xx+1,xt)-X;
        ys=lower_bound(Y+1,Y+yy+1,ys)-Y;yt=lower_bound(Y+1,Y+yy+1,yt)-Y;
        squ[xs][ys]=squ[xt][yt]=-1;
        for (int i=1;i<=n;++i)
            for (int j=1;j<=n;++j)
                if (i!=j&&a[i]>=a[j]&&b[i]>=b[j]&&c[i]<=c[j]&&d[i]<=d[j])
                {
                    flag[i]=1;
                    break;
                }
        for (int i=1;i<=n;++i)
        {
            if (flag[i]) continue;
            a[i]=lower_bound(X+1,X+xx+1,a[i])-X;
            c[i]=lower_bound(X+1,X+xx+1,c[i])-X;
            b[i]=lower_bound(Y+1,Y+yy+1,b[i])-Y;
            d[i]=lower_bound(Y+1,Y+yy+1,d[i])-Y;
            for (int j=a[i];j<=c[i];++j) squ[j][b[i]]=1,squ[j][d[i]]=2;
            for (int j=b[i];j<=d[i];++j) squ[a[i]][j]=1,squ[c[i]][j]=2;
            squ[a[i]][b[i]]=squ[a[i]][d[i]]=squ[c[i]][b[i]]=squ[c[i]][d[i]]=-1;
        }
        for (int i=1;i<=n;++i)
        {
            if (xs>a[i]&&ys>b[i]&&xsputs("No Path");
                return 0;
            }
            if (xt>a[i]&&yt>b[i]&&xtputs("No Path");
                return 0;
            }
        }
        for (int i=1;i<=n;++i)
        {
            if (flag[i]) continue;
            walk(a[i],b[i],-1,0);walk(a[i],b[i],0,-1);walk(a[i],d[i],-1,0);walk(a[i],d[i],0,1);
            walk(c[i],b[i],1,0);walk(c[i],b[i],0,-1);walk(c[i],d[i],1,0);walk(c[i],d[i],0,1);
            pt[a[i]][b[i]]=pt[a[i]][d[i]]=pt[c[i]][b[i]]=pt[c[i]][d[i]]=1;
        }pt[xs][ys]=pt[xt][yt]=1;
        walk(xs,ys,-1,0);walk(xs,ys,1,0);walk(xs,ys,0,-1);walk(xs,ys,0,1);
        walk(xt,yt,-1,0);walk(xt,yt,1,0);walk(xt,yt,0,-1);walk(xt,yt,0,1);
        for (int i=1;i<=xx;++i)
            for (int j=1;j<=yy;++j)
                if (pt[i][j])
                {
                    p[++cnt].x=i,p[cnt].y=j,p[cnt].id=cnt;
                    if (i==xs&&j==ys) s=cnt;
                    if (i==xt&&j==yt) t=cnt;
                }
        sort(p+1,p+cnt+1,cmpx);
        for (int i=1,j;i<=cnt;i=j)
        {
            j=i+1;
            while (j<=cnt&&p[j].x==p[i].x)
            {
                if (squ[p[j-1].x][p[j-1].y]==-1||squ[p[j].x][p[j].y]==-1
                ||squ[p[j-1].x][p[j-1].y]>=squ[p[j].x][p[j].y])
                    add(p[j-1].id,p[j].id,Dis(p[j-1],p[j]));
                ++j;
            }
        }
        sort(p+1,p+cnt+1,cmpy);
        for (int i=1,j;i<=cnt;i=j)
        {
            j=i+1;
            while (j<=cnt&&p[j].y==p[i].y)
            {
                if (squ[p[j-1].x][p[j-1].y]==-1||squ[p[j].x][p[j].y]==-1
                ||squ[p[j-1].x][p[j-1].y]>=squ[p[j].x][p[j].y])
                    add(p[j-1].id,p[j].id,Dis(p[j-1],p[j]));
                ++j;
            }
        }
        spfa();
        if (dis[t]!=inf) printf("%lld\n",dis[t]);
        else puts("No Path");
    }
}

再次感谢ATP的数据生成器!!!

#include
#include
#include
#include
#include
#include
#define random(x)(rand()*rand()%x+1)
#define N 10000
using namespace std;
int n,m1,m2,X[10010],Y[10010],Xx[10010],Yy[10010];
bool ext[10010][10010];
bool Ins(int x,int y,int xx,int yy,int i){
    if (x>=X[i]&&x<=Xx[i]&&y>=Y[i]&&y<=Yy[i]) return true;
    if (x>Xx[i]||xxreturn false;
    if (y>Yy[i]||yyreturn false;
    if (xxX[i]&&yyY[i]) return false;
    return true;
}
bool In(int x,int y){
    for (int i=1;i<=n;i++)
      if (x>X[i]&&xY[i]&&yreturn true;
    return false;
}
int main()
{
    freopen("input.txt","w",stdout);
    srand(time(0));
    int T=10;
    printf("%d\n",T);
    while (T--)
    {
        n=1000;m1=1;m2=1;
        memset(ext,false,sizeof(ext));
        for (int i=1;i<=n;){
            int x,y,xx,yy;
            bool flag=false;
            x=random(N);y=random(N);
            xx=random(N);yy=random(N);
            if (x>xx) swap(x,xx);
            if (y>yy) swap(y,yy);
            if (x==xx||y==yy) continue;
            for (int j=1;j<=i;j++)
              if (Ins(x,y,xx,yy,j)){
                  flag=true;break;
              }
            if (flag==true) continue;
            X[i]=x;Y[i]=y;Xx[i]=xx;Yy[i]=yy;++i;
        }
        for (int i=1;i<=m1;i++){
            int x=random(N),y=random(N);
            while (ext[x][y]==true||In(x,y)){
                x=random(N);y=random(N);
            }
            ext[x][y]=true;
            printf("%d %d ",x,y);
        }
        for (int i=1;i<=m2;i++){
            int x=random(N),y=random(N);
            while (ext[x][y]==true||In(x,y)){
                x=random(N);y=random(N);
            }
            ext[x][y]=true;
            printf("%d %d\n",x,y);
        }
        printf("%d\n",n);
        for (int i=1;i<=n;i++)
          printf("%d %d %d %d\n",X[i],Y[i],Xx[i],Yy[i]);
    }
    return 0;
}

你可能感兴趣的:(题解,图论算法,模拟)