[bzoj4456][分治][Dijkstra]旅行者

本文介绍了一种解决网格城市中两点间最短路径问题的高效算法。通过分治策略和Dijkstra算法结合,该方法能快速计算出任意两点间的最小通行时间,适用于大规模网格城市布局。

Description

小Y来到了一个新的城市旅行。她发现了这个城市的布局是网格状的,也就是有n条从东到西的道路和m条从南到北
的道路,这些道路两两相交形成n×m个路口 (i,j)(1≤i≤n,1≤j≤m)。她发现不同的道路路况不同,所以通过不
同的路口需要不同的时间。通过调查发现,从路口(i,j)到路口(i,j+1)需要时间 r(i,j),从路口(i,j)到路口(i+1
,j)需要时间c(i,j)。注意这里的道路是双向的。小Y有q个询问,她想知道从路口(x1,y1)到路口(x2,y2)最少需要 花多少时间。

Input

第一行包含 2 个正整数n,m,表示城市的大小。
接下来n行,每行包含m?1个整数,第i行第j个正整数表示从一个路口到另一个路口的时间r(i,j)。
接下来n?1行,每行包含m个整数,第i行第j个正整数表示从一个路口到另一个路口的时间c(i,j)。
接下来一行,包含1个正整数q,表示小Y的询问个数。 接下来q行,每行包含4个正整数 x1,y1,x2,y2,表示两个路口的位置。

Output

输出共q行,每行包含一个整数表示从一个路口到另一个路口最少需要花的时间。

Sample Input

2 2

2

3

6 4

2

1 1 2 2

1 2 2 1

Sample Output

6

7

题解

分治还是很ok的
对于一个方块,我们发现其中的两个点的最短路只有经过中轴线或者不经过中轴线
二分中轴线 设rx-lx>ry-ly 即我们二分x轴的中点
如果两个点被割开了,那么他们的最短路一定经过中轴线的某个点,这个裸跑dij
如果没有被割开,他们的最短路仍然可能经过某个点,可以分治查找
卡常大法好

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inline void print(int x){write(x);puts("");}
const int dx[4]={0,-1,0,1};
const int dy[4]={-1,0,1,0};
struct heap
{
    int c,x,y;
    heap(){}
    heap(int _c,int _x,int _y){c=_c;x=_x;y=_y;}
    friend bool operator <(heap n1,heap n2){return n1.c>n2.c;}
};
struct ask
{
    int s1,s2,e1,e2,ans,op;
}li[200005],li1[200005],li2[200005],li3[200005];
priority_queue<heap> q;
int tim,n,m,T;
int dis[210000],gg[210000],nw[210000];
int m1[210000],m2[210000];//行 列 
int pt(int x,int y){return (x-1)*m+y;}
inline bool check(int x,int y,int gx,int gy,int c)
{
    if(nw[pt(x,y)]!=tim)return true;
    return dis[pt(x,y)]>dis[pt(gx,gy)]+c;   
}
inline int getc(int x,int y,int k)
{
    if(k==0)return m2[pt(x,y-1)];
    else if(k==1)return m1[pt(x-1,y)];
    else if(k==2)return m2[pt(x,y)];
    else return m1[pt(x,y)];
}
inline void dijkstra(int sx,int sy,int lx,int rx,int ly,int ry)
{
    tim++;
    dis[pt(sx,sy)]=0;nw[pt(sx,sy)]=tim;
    q.push(heap(0,sx,sy));
    while(!q.empty())
    {
        heap tmp=q.top();q.pop();
        if(gg[pt(tmp.x,tmp.y)]==tim)continue;
        gg[pt(tmp.x,tmp.y)]=tim;
        for(int i=0;i<=3;i++)
        {
            int u=tmp.x+dx[i],v=tmp.y+dy[i];
            if(u<lx||u>rx||v<ly||v>ry)continue;
            int c=getc(tmp.x,tmp.y,i);
            if(check(u,v,tmp.x,tmp.y,c))
            {
                dis[pt(u,v)]=dis[pt(tmp.x,tmp.y)]+c;nw[pt(u,v)]=tim;
                q.push(heap(dis[pt(u,v)],u,v));
            }
        }
    }
}
void sol(int lx,int rx,int ly,int ry,int lq,int rq)
{
    if(lq>rq)return ;
    if(lx>rx||ly>ry)return ;
    if(lx==rx&&ly==ry)
    {
        for(int i=lq;i<=rq;i++)li[i].ans=0;
        return ;
    }
    if(rx-lx>ry-ly)
    {
        int mid=(rx+lx)/2;
        //printf("YES1: %d %d %d %d %d\n",lx,rx,ly,ry,mid);
        for(int i=ly;i<=ry;i++)
        {
            dijkstra(mid,i,lx,rx,ly,ry);
            for(int j=lq;j<=rq;j++)li[j].ans=min(li[j].ans,dis[pt(li[j].s1,li[j].s2)]+dis[pt(li[j].e1,li[j].e2)]);
        }
        int l1=0,l2=0,l3=0;
        for(int i=lq;i<=rq;i++)
        {
            if(li[i].s1>=lx&&li[i].s1<mid&&li[i].e1>=lx&&li[i].e1<mid)li1[++l1]=li[i];
            else if(li[i].s1>mid&&li[i].s1<=rx&&li[i].e1>mid&&li[i].e1<=rx)li2[++l2]=li[i];
            else li3[++l3]=li[i];
        }
        for(int i=1;i<=l1;i++)li[lq+i-1]=li1[i];
        for(int i=1;i<=l2;i++)li[lq+l1-1+i]=li2[i];
        for(int i=1;i<=l3;i++)li[lq+l1+l2-1+i]=li3[i];
        //for(int i=1;i<=l1;i++)printf("CASE1: %d %d %d %d\n",li1[i].s1,li1[i].s2,li1[i].e1,li1[i].e2);
        //for(int i=1;i<=l2;i++)printf("CASE2: %d %d %d %d\n",li2[i].s1,li2[i].s2,li2[i].e1,li2[i].e2);
        //puts("");
        sol(lx,mid-1,ly,ry,lq,lq+l1-1);sol(mid+1,rx,ly,ry,lq+l1,lq+l1+l2-1);
    }
    else
    {
        int mid=(ry+ly)/2;
        //printf("YES2: %d %d %d %d %d\n",lx,rx,ly,ry,mid);
        for(int i=lx;i<=rx;i++)
        {
            dijkstra(i,mid,lx,rx,ly,ry);
            for(int j=lq;j<=rq;j++)li[j].ans=min(li[j].ans,dis[pt(li[j].s1,li[j].s2)]+dis[pt(li[j].e1,li[j].e2)]);
        }
        int l1=0,l2=0,l3=0;;
        for(int i=lq;i<=rq;i++)
        {
            if(li[i].s2>=ly&&li[i].s2<mid&&li[i].e2>=ly&&li[i].e2<mid)li1[++l1]=li[i];
            else if(li[i].s2>mid&&li[i].s2<=ry&&li[i].e2>mid&&li[i].e2<=ry)li2[++l2]=li[i];
            else li3[++l3]=li[i];
        }
        for(int i=1;i<=l1;i++)li[lq+i-1]=li1[i];
        for(int i=1;i<=l2;i++)li[lq+i+l1-1]=li2[i];
        for(int i=1;i<=l3;i++)li[lq+l1+l2-1+i]=li3[i];
        //for(int i=1;i<=l1;i++)printf("CASE1: %d %d %d %d\n",li1[i].s1,li1[i].s2,li1[i].e1,li1[i].e2);
        //for(int i=1;i<=l2;i++)printf("CASE2: %d %d %d %d\n",li2[i].s1,li2[i].s2,li2[i].e1,li2[i].e2);
        //puts("");
        sol(lx,rx,ly,mid-1,lq,lq+l1-1);sol(lx,rx,mid+1,ry,lq+l1,lq+l1+l2-1);
    }
}
bool cmp(ask n1,ask n2){return n1.op<n2.op;}
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++)for(int j=1;j<m;j++)m2[pt(i,j)]=read();
    for(int i=1;i<n;i++)for(int j=1;j<=m;j++)m1[pt(i,j)]=read();
    scanf("%d",&T);
    for(int i=1;i<=T;i++)li[i].s1=read(),li[i].s2=read(),li[i].e1=read(),li[i].e2=read(),li[i].op=i,li[i].ans=(1<<31-1);
    memset(dis,63,sizeof(dis));
    sol(1,n,1,m,1,T);
    sort(li+1,li+1+T,cmp);
    for(int i=1;i<=T;i++)print(li[i].ans);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值