汽车加油行驶问题
题目描述
给定一个 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。
输入
第一行是 N,K,A,B,C的值。第二行起是一个 N*N 的 0-1 方阵,每行 N 个值,至 N+1 行结束。方阵的第 i 行第 j 列处的值为 1 表示在网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。各行相邻两个数以空格分隔。
输出
将最小费用输出。
样例输入
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
提示
(none)
AC代码
#include <iostream>
#include <string.h>
using namespace std;
int n,k,a,b,c;
int g[101][101];
struct Point
{
int x,y,o,ans;
};
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int dm[4] = {0,1,0,1};//dm是控制回走的时候是否收费
int f,e;
Point q[7000000],s;
bool used[101][101][11];
int m[101][101][11];
int main()
{
cin >> n >>k >> a >>b >> c;//n是n行n列的矩阵 k是加满油后能行的路程 a是加油的费用
//b是倒退的费用 c造加油站的费用
for (int i=1;i <= n;i++)//将矩阵转换为数组
for (int j = 1;j <= n;j++)
scanf("%d",&g[i][j]);//输入
s.x= 1,s.y = 1,s.o = k,s.ans = 0;//s.x是起点的x坐标 s.y是起点的y坐标
//s.o就是汽车可以行驶的剩余路程 s.ans是从起点到此地的钱数
memset(used,0,sizeof(used));//memset()把used[][][]全部初始化为0
used[1][1][k] = 1;//把起点标为已经走过,(并记录剩余的路程为k)
m[1][1][k] = 0;//走到这里时一共用的钱数 (并记录剩余的路程为k)
f=1,e=1;//f出队 e入队
q[1]=s;//把s入队
while (f <= e)//防止越界
{
Point u = q[f++];//u是选中的元素
for (int i = 0;i < 4;i ++)//重复向4个方向拓展
{
Point v = u;//v是由u拓展而来
v.x = u.x + dx[i];//向4个方向拓展
v.y = u.y + dy[i];//up
v.o = u.o - 1;//剩余路程 -1
v.ans = u.ans + dm[i] * b;//v.ans是钱数 u.ans + dm[i] * b 是 加上回走的费用
//dm[i]是1 , 表示回走 dm[i]是0 表示不回走
if (v.x < 1 || v.x > n || v.y < 1 || v.y > n) continue;//保证这些拓展而来的坐标在棋盘上
if (v.o < 0) continue;//剩余路程 > 0才可以继续行驶
if (g[v.x][v.y] == 1) //如果是加油站 就要加油
{
v.ans += a;//加上加油费
v.o = k;//重新计算可行驶的路程
if (used[v.x][v.y][v.o] == 0 || (used[v.x][v.y][v.o] == 1 && v.ans < m[v.x][v.y][v.o]))
{
used[v.x][v.y][v.o] = 1;//把这个点标为走过
m[v.x][v.y][v.o] = v.ans;//把现在的钱数赋值(弄)到总钱数里面
//m[][][]是存储所有点的钱数的数组
//是否可以不用数组的方式,用一个变量每次比较并覆盖
e ++;//e是入队下标
q[e] = v;//把v入队
}
}
if (g[v.x][v.y]==0) //如果不是加油站
{
if(v.o>=0)//还可以开(还有油)
{
if (used[v.x][v.y][v.o] == 0 || (used[v.x][v.y][v.o] == 1 && v.ans < m[v.x][v.y][v.o]))
{
used[v.x][v.y][v.o] = 1;//已使用
m[v.x][v.y][v.o] = v.ans;//把现在的钱数赋值(弄)到总钱数里面
e ++;//e是入队下标
q[e] = v;//把v入队
}
}
v.ans+=a+c,v.o=k;//造加油站 付钱 重新计算可行驶的路程
if (used[v.x][v.y][v.o] == 0 || (used[v.x][v.y][v.o] == 1 && v.ans < m[v.x][v.y][v.o]))
{
used[v.x][v.y][v.o] = 1;//已使用
m[v.x][v.y][v.o] = v.ans;
e ++;
q[e] = v;
}
}
}
}
int res = 1000000000;//把res设成最大,取最小值
for (int i = 0;i <= k ;i++)//重复计算
if (res > m[n][n][i] && m[n][n][i] != 0) res = m[n][n][i];//取最小值
cout << res << endl;//将最后一个点的费用输出(即最小费用)
return 0;
}
(未完待续)