题目链接
https://codeforces.com/contest/1520/problem/G
题意
给出网格,格子中有墙。向相邻无墙位置移动花费m,有的位置有权值w,这样的点是传送门,可以从一个传送门传到另一个传送门,花费w1+w2。问左上到右下花费。
思路
最开始以为是一个裸的最短路,通过技巧将O(n²)边降到O(n)即可。就是每个点向周围可移动连边,建立sp点,每个传送门向他连边权为w,跑最短路即可。正确性没问题,但会MLE(2e7的数组RE,再大一点就MLE,真是麻了)。
观察发现,传送门最多只是用一次。为什么? 假如四个门a,b,c,d。我们从a-b传送,再走到c-d传送,花费为wa+wb+wc+wd,其一定比直接从a传到d花费wa+wd更劣。因此,我们有两种走法,第一种是直接走,不传送,那么他的花费bfs跑一下就行了(边权权为m,根本不需要最短路算法)。第二种是走一定距离,再传送。我们枚举所有点,处理出这样两个数据。bef:从起点出发,到这个点进入传送门的花费。aft:从这个点离开传送门,前往终点的花费。为了处理bef,我们还需要反向跑一遍bfs。之后bef+aft就是第二种方法的答案,两种答案取一个min即可。
复杂度
O ( n m ) O(nm) O(nm)
教训/收获
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxe=30005050;
const int maxn=5000060;
const ll inf=0x3f3f3f3f3f3f3f3f;
int n,m,k;
int mp[2005][2005];
int dxy[][2]={{1,0},{-1,0},{0,1},{0,-1}};
bool vis[maxn];
int tran(int x,int y){
return (x-1)*m+y;
}
bool judge(int x,int y){
return x>=1&&y>=1&&x<=n&&y<=m&&mp[x][y]!=-1&&vis[tran(x,y)]==0;
}
ll dis1[maxn],dis2[maxn];
void bfs(int x,int y,ll *dis){
memset(vis,0,sizeof vis);
queue<pair<int,int>>q;
pair<int,int> p;
vis[ tran(x,y) ]=true;
q.push({x,y});
dis[tran(x,y)]=0;
while(q.size()){
auto now=q.front();q.pop();
for(int i=0;i<4;i++){
int dx=now.first+dxy[i][0];
int dy=now.second+dxy[i][1];
if(judge(dx,dy)){
vis[tran(dx,dy)]=1;
q.push({dx,dy});
dis[tran(dx,dy)]=dis[tran(now.first,now.second)]+k;
}
}
}
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
memset(dis1,0x3f,sizeof dis1);
memset(dis2,0x3f,sizeof dis2);
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp[i][j];
}
}
bfs(1,1,dis1);
bfs(n,m,dis2);
ll ans=dis1[tran(n,m)];
ll bef=inf,aft=inf;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mp[i][j]==-1) continue;
if(mp[i][j]!=0){
bef=min(bef,dis1[tran(i,j)]+mp[i][j]);
aft=min(aft,dis2[tran(i,j)]+mp[i][j]);
}
}
}
ans=min(ans,bef+aft);
if(ans==inf)
cout<<-1<<endl;
else
cout<<ans<<endl;
}