题意:给出一个字符矩阵,#代表不可走,D代表经过此处不算距离,c代表经过此处距离+1,给出一个起点,要求求出到矩阵边界处的D的最短距离,矩阵边界一定由#和D组成
思路:bfs,每一个点不一定只更新一次,所以不需要vis数组,松弛每个点时如果此点到起始点的距离+到临点的距离大于临点到起始点的距离,则更新临点的距离,并将临点更新后的状态加入队列,(bfs中,队列存储的是更新过的点,也就是需要松弛的点),每次到矩阵边界如果此处为D,则更新结果,结果取最小值
实际上这题就是一个多终点的最短路,这个bfs跟最短路的代码有点像
ac代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N=410;
int n,m,x,y;
char mp[N][N];
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
struct Node//更新队列中的点要记录坐标以及更新过后的距离
{
int x,y;
int d;
};
int dist[N][N];//没个点到起点的最小距离
int bfs()
{
int res=INF;
queue<Node> q;//q中存储的是更新过的点,也就是距离变小的点,也就需要用这些距离变小的点去更新他的临点
memset(dist,INF,sizeof dist);
dist[x][y]=1;//初始点也算一个移动,所以初始化为1
q.push({x,y,dist[x][y]});
while(q.size())
{
auto t=q.front();
q.pop();
if(t.d>=res)continue;//如果当前点的最小距离已经大于已知结果的话则不用再更新了(此处优化可以加快一倍的速度)
for(int i=0;i<4;i++)
{
int tx=t.x+dx[i],ty=t.y+dy[i];//移动到下一位置
if(mp[tx][ty]=='#'||tx<0||tx>=n||ty<0||ty>=m)continue;//非法位置跳过
if(tx==0||tx==n-1||ty==0||ty==m-1)res=min(res,t.d);
int dd=t.d;//dd代表临点到起点的距离,如果下一个点为D则距离相等
if(mp[tx][ty]=='c')dd++;//如果这个点为c则距离+1
if(dd<dist[tx][ty])//如果距离小于当前此点距离起点的距离,则更新掉此点的距离,更新之后加入更新队列
{
dist[tx][ty]=dd;
q.push({tx,ty,dist[tx][ty]});
}
}
}
return res;
}
void solve()
{
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>mp[i][j];
cin>>x>>y;
x--,y--;//输入给的是从1,1开始的点,而此处的图是从0,0开始存的,所以要-1
int res=bfs();
if(res!=INF)cout<<res<<endl;
else cout<<0<<endl;//如果结果为正无穷的话则说明没有更新出合法结果,则输出0
}
signed main()
{
Mirai;
int T=1;
//cin>>T;
while(T--)
{
solve();
}
}