[BZOJ2143]飞飞侠 并查集优化最短路

链接

题解

首先很容易想到对每个点暴力跑Dijkstra,但是这样边数是 \(N^4\) 的,考虑优化

这题有个性质,同一个点到其他点的边权是一样的(这个点的点权),现在考虑对Dijkstra算法作出如下改进:

将点插入堆时,比较 \(dis[i]+w[i]\) 而不是 \(dis[i]\) ,这样可以保证一个点被更新后不会再一次被更新,同时仍然用原来证明Dijkstra正确性的贪心方法证明该算法仍然可以得到最短路。

因此,用并查集合并已经更新过的点,再一次扫描到的时候,直接跳过即可,边数优化成 \(N^2\)

复杂度 \(O(N^2logN)\)

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
inline int read(){char c,p=0;int w;
    while(isspace(c=getchar()));if(c=='-')p=1,c=getchar();w=c&15;
    while(isdigit(c=getchar()))w=w*10+(c&15);return p?-w:w;
}
template<typename T,typename U>inline bool smin(T&x,const U&y){return x>y?x=y,1:0;}
template<typename T,typename U>inline bool smax(T&x,const U&y){return x<y?x=y,1:0;}
const int N=155;
int n,m,a[N][N],b[N][N],d[3][N][N],vis[N][N],fa[N][N];
priority_queue< pair<int,pii> >q;
#define xx first
#define yy second
inline int find(int*f,int x){return f[x]?f[x]=find(f,f[x]):x;}
inline void solve(int d[N][N],pii s){
    REP(i,1,n)REP(j,1,m)d[i][j]=1e9;
    memset(vis,0,sizeof vis);
    memset(fa,0,sizeof fa);
    d[s.xx][s.yy]=0;
    q.push(make_pair(-a[s.xx][s.yy],s));fa[s.xx][s.yy]=s.yy+1;
    while(!q.empty()){
        pii u=q.top().yy;q.pop();
        const int&x=u.xx,&y=u.yy;
        if(vis[x][y])continue;vis[x][y]=1;
        int lx=max(1,x-b[x][y]),rx=min(n,x+b[x][y]);
        REP(i,lx,rx){
            int t=b[x][y]-abs(i-x),ly=max(1,y-t),ry=min(m,y+t);
            for(int j=find(fa[i],ly);j<=ry;j=find(fa[i],j)){
                if(smin(d[i][j],d[x][y]+a[x][y]))q.push(make_pair(-d[i][j]-a[i][j],pii(i,j)));
                fa[i][j]=j+1;
            }
            
        }
    }
}
pii s[3];
const char ss[]="XYZ";
int main(){
    n=read(),m=read();
    REP(i,1,n)REP(j,1,m)b[i][j]=read(); 
    REP(i,1,n)REP(j,1,m)a[i][j]=read();
    REP(i,0,2)s[i].xx=read(),s[i].yy=read(),solve(d[i],s[i]);
    int dis=1e9;
    int ans=-1;
    REP(i,0,2){ 
        #define d(p) d[p][s[i].xx][s[i].yy]
        if(smin(dis,d(0)+d(1)+d(2)))ans=i;
    }
    if(~ans)printf("%c\n%d",ss[ans],dis);
    else puts("NO");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/HolyK/p/9858182.html
今日推荐