【一句话题意】给一个图,有一些可以空岛可以落脚。左右相邻的空岛之间移动不需要梯子,在同一列的空岛之间上下移动需要长度大于等于高度差的梯子。问从起点到终点至少需要多长的梯子。
【算法一】鉴于拥有固定长度的梯子,跑一遍bfs确定是否可行的复杂度是O(n)的,且答案关于梯子长度单调。我们可以二分答案,二分梯子的长度再check。总复杂度为O(nlogn)
【算法二】格子图的求到某个点的最小值,一般可以用各种的spfa变种完成。总复杂度O(玄学)
【算法三】把所有空岛连边,跑最小生成树。每加进一条边就检查起点、终点是否在同一集合。注意上下只加相邻的空岛。总复杂度O(nlogn)
给出算法三代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rint register int
using namespace std;
const int maxn=1051;
int n,m,tot;
bool g[maxn][maxn];
char s[maxn];
struct Edge{
int u,v,c;
}edge[maxn*maxn];
int f[maxn*maxn];
bool cmp(Edge x,Edge y){
return x.c<y.c;
}
inline int find(int x){if(f[x]!=x)f[x]=find(f[x]);return f[x];}
int main(){
freopen("mario.in","r",stdin);
freopen("mario.out","w",stdout);
cin>>n>>m;
for(rint i=1;i<=n;i++){
scanf("%s",s);
for(rint j=0;j<m;j++)
if(s[j]=='_')g[i][j+1]=0;
else g[i][j+1]=1;
}
int x,y,e1,e2;
cin>>x>>y;e1=(n-1)*m+1,e2=(x-1)*m+y;
for(rint i=1;i<=n*m;i++)f[i]=i;
for(rint i=1;i<=n;i++)
for(rint j=1;j<m;j++)
if(g[i][j]&&g[i][j+1])edge[++tot]=(Edge){(i-1)*m+j,(i-1)*m+j+1,0};
for(rint j=1;j<=m;j++){
rint p1=1,p2;
while(p1<=n&&!g[p1][j]) p1++;
p2=p1+1;
while(p2<=n){
if(g[p2][j])edge[++tot]=(Edge){(p1-1)*m+j,(p2-1)*m+j,p2-p1},p1=p2;
p2++;
}
}
sort(edge+1,edge+tot+1,cmp);
for(rint i=1;i<=tot;i++){
f[find(edge[i].u)]=find(edge[i].v);
if(find(e1)==find(e2)){
printf("%d\n",edge[i].c);
return 0;
}
}
return 0;
}