题解:P4009 汽车加油行驶问题

题目

思路

这是一个分层图最短路问题,我们可以使用升维的方法来完成本题。因为存在加油付费的问题,边权不一定为 1 1 1,所以不能使用广搜来做。数据范围不大: N ≤ 100 N \le 100 N100。可以使用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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值