[BZOJ2143]飞飞侠 并查集优化最短路
2024-08-31 16:28:22
题解
首先很容易想到对每个点暴力跑Dijkstra,但是这样边数是 \(N^4\) 的,考虑优化
发现每次松弛的时候,都要把整个地图扫一遍,每个节点都要重复扫很多次,如果我们在一个点不会再被更新的时候,用并查集跳过去,那么就可以降低复杂度
如果将点插入堆时,比较 \(dis[i]+w[i]\) 而不是 \(dis[i]\) ,这样可以保证一个点被更新后不会再一次被更新。
现在证明上述结论,以及这样做仍然可以得到正确的最短路。
假设点 \(x\) 已经得到了最短路,证明用该点更新的 \(y\) 也得到了最短路
反证,假设存在路径 $ x’ \to y$ 使 \(dis[y]\) 更小,且在 \(x\) 更新 \(y\) 之后,那么有 \(dis[x']+w[x']< dis[x]+w[x]\) ,因为 \(x'\) 在 \(x\) 之后,有 \(dis[x']+w[x']\ge dis[x]+w[x]\),两式矛盾,运用数学归纳法,可知上述结论成立,以及起点 \(s\) 到每一点的最短路径就是 \(dis[i]\)
因此,用并查集合并已经更新过的点,再一次扫描到的时候,直接跳过即可,边数优化成 \(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;
}
最新文章
- final 评论 I
- 移动Web单页应用开发实践——页面结构化
- Android开发在使用第三方推送的时候出现INSTALL_FAILED_VERSION_DOWNGRADE
- P2P直播、点播技术学习经验
- ASP.NET MVC- 使用PageList.Mvc分页
- DHCP Option 60 的理解
- springMVC学习(1)
- C#进程间通信--API传递参数(SendMessage)
- setStyleSheet来设定窗口部件的样式
- UIWebView 使用要注意的几点
- 自然语言处理高手_相关资源_开源项目(比如:分词,word2vec等)
- Windows 10 IoT Serials 9 – 如何利用IoTCoreAudioControlTool改变设备的音频设备
- C++map类型 之 简单介绍
- 配置SQL Server on Linux(1)
- 从零开始一起学习SLAM | 掌握g2o边的代码套路
- HTML的Tomcat
- android与php使用base64加密的字符串结果不一样解决方法
- 莫烦keras学习自修第一天【keras的安装】
- layer.open窗口自适应问题
- gulp常用方法
热门文章
- django uWSGI nginx搭建一个web服务器 确定可用
- Frame Stacking ZOJ 1083,poj 1128
- sicily 1004. 简单哈希
- 基于jquery 的find()函数和children()函数的区别
- P3066 [USACO12DEC]逃跑的BarnRunning Away From… 树上差分_树上倍增
- iOS下调用元素的focus方法,input元素不聚焦,键盘不弹起的问题
- 在MAC上安装lxml到Python3
- caioj 1072 动态规划入门(二维一边推5:最长公共子序列 LCSS加强版)
- 【Codeforces Round #465 (Div. 2) C】Fifa and Fafa
- javaweb——登陆权限过滤器的编写