[BZOJ 2304][Apio2011]寻路:SPFA

xiaoxiao2021-02-27  370

点击这里查看原题

首先可以想到这题求最短路,那肯定是SPFA(没有负权边,Dijkstra当然也可以啦),难点在于如何构图。 将坐标离散化,然后将矩形的边和顶点分别做不同的标记。从每个矩形的四个顶点出发,将到达的第一个有标记的点(也就是其他矩形上的点)标记为mark[i][j]=1。 将所有mark[i][j]=1或是矩形的四个顶点的点加入,在同一行或同一列的点之间连边,然后做SPFA即可。 因为题目保证了数据的合法性,因此不需要特判非法情况(比如起点或终点在矩形内、矩形相互包含等)

/* User:Small Language:C++ Problem No.:2304 */ #include<bits/stdc++.h> #define ll long long using namespace std; const int M=1e3+5; int n,xs,ys,xt,yt,a[M],b[M],c[M],d[M],x[M<<1],y[M<<1],cntx,cnty,cnt,tot,squ[M<<1][M<<1],fir[24005],s,t; bool vis[24005],mark[M<<1][M<<1]; ll dis[24005],inf; struct edge{ int v,w,nex; }e[200005]; struct po{ int x,y,id; }p[24005]; bool cmpx(po a,po b){ return a.x!=b.x?a.x<b.x:a.y<b.y; } bool cmpy(po a,po b){ return a.y!=b.y?a.y<b.y:a.x<b.x; } void init(){ memset(fir,0,sizeof(fir)); memset(mark,0,sizeof(mark)); memset(squ,0,sizeof(squ)); cnt=tot=cntx=cnty=0; } void add(int u,int v,int w){ e[++tot]=(edge){v,w,fir[u]}; fir[u]=tot; e[++tot]=(edge){u,w,fir[v]}; fir[v]=tot; } void walk(int x,int y,int dx,int dy){ x+=dx; y+=dy; while(!squ[x][y]){ if(x<1||x>cntx||y<1||y>cnty) return; x+=dx; y+=dy; } mark[x][y]=1; } int getdis(po &a,po &b){ return abs(x[a.x]-x[b.x])+abs(y[a.y]-y[b.y]); } void spfa(){ queue<int> q; memset(dis,127,sizeof(dis)); inf=dis[0]; dis[s]=0; vis[s]=1; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(int i=fir[u];i;i=e[i].nex){ int v=e[i].v; if(dis[v]>dis[u]+e[i].w){ dis[v]=dis[u]+e[i].w; if(!vis[v]){ vis[v]=1; q.push(v); } } } } } void solve(){ init(); scanf("%d%d%d%d",&xs,&ys,&xt,&yt); scanf("%d",&n); x[++cntx]=xs; x[++cntx]=xt; y[++cnty]=ys; y[++cnty]=yt; 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[++cntx]=a[i]; x[++cntx]=c[i]; y[++cnty]=b[i]; y[++cnty]=d[i]; } sort(x+1,x+cntx+1); sort(y+1,y+cnty+1); cntx=unique(x+1,x+cntx+1)-x-1; cnty=unique(y+1,y+cnty+1)-y-1; xs=lower_bound(x+1,x+cntx+1,xs)-x; xt=lower_bound(x+1,x+cntx+1,xt)-x; ys=lower_bound(y+1,y+cnty+1,ys)-y; yt=lower_bound(y+1,y+cnty+1,yt)-y; squ[xs][ys]=squ[xt][yt]=-1;//-1表示是顶点 for(int i=1;i<=n;i++){ a[i]=lower_bound(x+1,x+cntx+1,a[i])-x; c[i]=lower_bound(x+1,x+cntx+1,c[i])-x; b[i]=lower_bound(y+1,y+cnty+1,b[i])-y; d[i]=lower_bound(y+1,y+cnty+1,d[i])-y; for(int j=a[i];j<=c[i];j++) squ[j][b[i]]=1,squ[j][d[i]]=2;//1是左边或上边,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++){//扩展可以走到的点 mark[a[i]][b[i]]=mark[a[i]][d[i]]=mark[c[i]][b[i]]=mark[c[i]][d[i]]=1; walk(a[i],b[i],0,-1);walk(a[i],b[i],-1,0); walk(a[i],d[i],-1,0);walk(a[i],d[i],0,1); walk(c[i],b[i],0,-1);walk(c[i],b[i],1,0); walk(c[i],d[i],0,1);walk(c[i],d[i],1,0); } mark[xs][ys]=mark[xt][yt]=1; walk(xs,ys,0,1);walk(xs,ys,0,-1);walk(xs,ys,1,0);walk(xs,ys,-1,0); walk(xt,yt,0,1);walk(xt,yt,0,-1);walk(xt,yt,1,0);walk(xt,yt,-1,0); for(int i=1;i<=cntx;i++){ for(int j=1;j<=cnty;j++){ if(!mark[i][j]) continue; p[++cnt]=(po){i,j,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[i].x==p[j].x){//在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,getdis(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[i].y==p[j].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,getdis(p[j-1],p[j])); j++; } } spfa(); if(dis[t]>=inf) printf("No Path\n"); else printf("%lld\n",dis[t]); } int main(){ freopen("data.in","r",stdin);// int kas; scanf("%d",&kas); while(kas--) solve(); return 0; }
转载请注明原文地址: https://www.6miu.com/read-1790.html

最新回复(0)