思路
这是一个分层图最短路问题,我们可以使用升维的方法来完成本题。因为存在加油付费的问题,边权不一定为 1 1 1,所以不能使用广搜来做。数据范围不大: N ≤ 100 N \le 100 N≤100。可以使用SPFA算法完成本题。每一个状态有三个值,分别是当前到达的行、列,以及剩下的油还能走几步。考虑是否需要加油,计算边权即可。
伪代码/框架
定义一个结构体,用来存放当前到达的行、列,以及剩下的油还能走几步。
SPFA函数:
- 我们使用一个结构体类型的队列,存放状态。
- 用 v i s vis vis 数组记录状态是否在队列中。
- 用 d i s dis dis 数组记录从初始状态到当前状态的最小花费。
- 初始状态: x = 1 , y = 1 , z = K x=1,y=1,z=K x=1,y=1,z=K。
- 枚举每个状态往四周走的情况,计算边权,入队或更新最小花费。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int n, k, a, b, c;
int g[110][110];
int dis[110][110][20];
int vis[110][110][20];
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
struct node
{
int x, y, z;
} ;
void spfa(int sx, int sy)
{
queue<node> q;
memset(dis, 0x3f, sizeof(dis));
q.push({sx, sy, k});
dis[sx][sy][k] = 0;
vis[sx][sy][k] = 1;
while (q.size())
{
int x = q.front().x;
int y = q.front().y;
int z = q.front().z;
q.pop();
vis[x][y][z] = 0;
if (z <= 0) continue;
for (int i = 0; i <= 3; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 1 || nx > n)
continue;
if (ny < 1 || ny > n)
continue;
int t = (1 - i % 2) * b;
if (g[nx][ny] && dis[nx][ny][k] > dis[x][y][z] + t + a)
{
dis[nx][ny][k] = dis[x][y][z] + t + a;
if (vis[nx][ny][k] == 0)
{
q.push({nx, ny, k});
vis[nx][ny][k] = 1;
}
}
else
{
if (dis[nx][ny][z - 1] > dis[x][y][z] + t)
{
dis[nx][ny][z - 1] = dis[x][y][z] + t;
if (vis[nx][ny][z - 1] == 0)
{
q.push({nx, ny, z - 1});
vis[nx][ny][z - 1] = 1;
}
}
if (dis[nx][ny][k] > dis[x][y][z] + t + a + c)
{
dis[nx][ny][k] = dis[x][y][z] + t + a + c;
if (vis[nx][ny][k] == 0)
{
q.push({nx, ny, k});
vis[nx][ny][k] = 1;
}
}
}
}
}
}
int main()
{
cin >> n >> k >> a >> b >> c;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> g[i][j];
spfa(1, 1);
int minn = 1e9;
for (int i = 0; i <= k; i++)
minn = min(minn, dis[n][n][i]);
cout << minn << endl;
return 0;
}