[题解] PowerOJ 1750 汽车加油行驶问题 (最短路)
- 传送门 -
https://www.oj.swust.edu.cn/problem/show/1750
Time Limit: 1000 MS Memory Limit: 65536 KB
Total Submit: 55 Accepted: 31 Page View: 824
Description
给定一个N*N 的方形网格,设其左上角为起点◎,坐标为(1,1),X 轴向右为正,Y 轴向下为正,每个方格边长为1,如图所示。一辆汽车从起点◎出发驶向右下角终点▲,其 坐标为(N,N)。在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在 行驶过程中应遵守如下规则: (1)汽车只能沿网格边行驶,装满油后能行驶K 条网格边。出发时汽车已装满油,在起 点与终点处不设油库。 (2)汽车经过一条网格边时,若其X 坐标或Y 坐标减小,则应付费用B,否则免付费用。 (3)汽车在行驶过程中遇油库则应加满油并付加油费用A。 (4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费用A)。 (5)(1)~(4)中的各数N、K、A、B、C均为正整数,且满足约束:2<=N<=100,2<=K<=10。 设计一个算法,求出汽车从起点出发到达终点的一条所付费用最少的行驶路线。 对于给定的交通网格,计算汽车从起点出发到达终点的一条所付费用最少的行驶路线。
Input
由文件input.txt提供输入数据。文件的第一行是N,K,A,B,C的值。第二行起是一 个N*N 的0-1 方阵,每行N 个值,至N+1 行结束。方阵的第i 行第j 列处的值为1 表示在 网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。各行相邻两个数以空格分隔。
Output
程序运行结束时,将最小费用输出到文件output.txt中。
9 3 2 3 6
0 0 0 0 1 0 0 0 0
0 0 0 1 0 1 1 0 0
1 0 1 0 0 0 0 1 0
0 0 0 0 0 1 0 0 1
1 0 0 1 0 0 1 0 0
0 1 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0 1
1 0 0 1 0 0 0 1 0
0 1 0 0 0 0 0 0 0
12
Source
- 思路 -
分层建模.
以剩余油量\((0...k)\)来分层.
每一层包含所有节点.
对于某层节点 \(x\) 来说:
若该层油量不满:
有加油站时: 连一条权值为 \(a\) 的边指向满油量的那一层的 \(x\) 点.
无加油站时: 连一条权值为 \(a+c\) 的边指向满油量的那一层的 \(x\) 点.
若该层油量满:
向四个方向连边.
若该层油量不满:
有加油站时: 没有操作.(此时必须加油)
无加油站时: 向四个方向连边.
然后求最短路.
起点为\(<1,1,k>\), 终点为\(<n,n,x>,\ \ 0\leq x\leq k\).
细节见代码.
- 代码 -
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 2e5 + 50;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
int NXT[M], TO[M], V[M];
int DIS[N], VIS[N], HD[N];
int MAP[205][205];
int C[205][205][15];
int ss, tt, sz, k, n, m, a, b, c, tot;
queue<int> q;
int mk(int x, int y, int z) {
return z * n * n + (x - 1) * n + y;
}
void add(int x, int y, int z) {
TO[sz] = y; V[sz] = z;
NXT[sz] = HD[x]; HD[x] = sz++;
}
int spfa() {
memset(DIS, 0x3f, sizeof (DIS));
DIS[ss] = 0;
q.push(ss);
while (!q.empty()) {
int u = q.front();
VIS[u] = 0;
q.pop();
for (int i = HD[u]; i != -1; i = NXT[i]) {
int v = TO[i];
if (DIS[v] > DIS[u] + V[i]) {
DIS[v] = DIS[u] + V[i];
if (!VIS[v]) {
VIS[v] = 1;
q.push(v);
}
}
}
}
return DIS[tt];
}
int main() {
memset(HD, -1, sizeof (HD));
scanf("%d%d%d%d%d", &n, &k, &a, &b, &c);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
scanf("%d", &MAP[i][j]);
tt = 200010;
for (int x = 1; x <= n; ++x) {
for (int y = 1; y <= n; ++y) {
for (int i = k; i >= 0; --i) {
tot = mk(x, y, i);
if (x == 1 && y == 1 && i == k)
ss = tot;
if (x == n && y == n)
add(tot, tt, 0);
if (i != k)
if (MAP[x][y]) add(tot, mk(x, y, k), a);
else add(tot, mk(x, y, k), a + c);
if (i != 0) {
if (MAP[x][y] && i != k) continue; //注意避开这种情况
if (x != 1)
add(tot, mk(x - 1, y, i - 1), b);
if (y != 1)
add(tot, mk(x, y - 1, i - 1), b);
if (x != n)
add(tot, mk(x + 1, y, i - 1), 0);
if (y != n)
add(tot, mk(x, y + 1, i - 1), 0);
}
}
}
}
printf("%d\n", spfa());
return 0;
}