题目描述
给定一个 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.txt提供输入数据。文件的第一行是 N,K,A,B,C的值。第二行起是一个 N*N 的 0-1 方阵,每行 N 个值,至 N+1 行结束。方阵的第 i 行第 j 列处的值为 1 表示在网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。各行相邻两个数以空格分隔。
输出格式
程序运行结束时,将最小费用输出到文件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
题目分析
这题比上题[网络流24题-14] 孤岛营救 - 分层图 简单那么一点点,啧啧啧。
一般使用分层图的特点是有一维比较小,并且去掉限制条件是标准的图论模型,于是按照比较小的那一维分层。
此题就是按照油量分层,建图如下:
1.若当前点是加油站,从当前点当前油量连接一条跨维边至此点满油量,边权为a
2.若当前点不是加油点,且已无油,从当前点连接一条跨维边至此点满油量,边权为a+c(c不包含加油费用)
3.从每个加油点连接一条跨维边至周围四点油量-1处,若横坐标或纵坐标减少,则边权为b,否则边权为0
4.从每个非加油点枚举油量连接一条跨维边至周围四点油量-1处,若横坐标或纵坐标减少,则边权为b,否则边权为0
源代码
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=200005; //数组范围
struct Edge { //前向星
int from,to,dist;
};
struct HeapNode {
int d,u; //u为当前结点
bool operator < (HeapNode a) const {
return d>a.d;
}
};
struct Dijkstra {
int n,m;
vector<Edge> edges; //邻接表
vector<int> G[maxn]; //记录每个结点可以到达的结点
bool vst[maxn];
int dist[maxn];
void init(int n) {
this->n=n;
for(int i=1; i<=n; i++)G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int dist) {
edges.push_back((Edge) {
from,to,dist
});
m=edges.size();
G[from].push_back(m-1);
}
void main(int s) { //核心算法
priority_queue<HeapNode> Q;
for(int i=1; i<=n; i++)dist[i]=0x7fffffff/2;
dist[s]=0;
memset(vst,0,sizeof(vst));
Q.push((HeapNode) {
0,s
});
while(!Q.empty()) {
HeapNode Now=Q.top();
Q.pop();
if(vst[Now.u])continue;
vst[Now.u]=1;
for(int i=0; i<G[Now.u].size(); i++) {
Edge& e=edges[G[Now.u][i]]; //边的信息
int Next=e.to;
if(dist[Next]>dist[Now.u]+e.dist) {
dist[Next]=dist[Now.u]+e.dist;
Q.push((HeapNode) {
dist[Next],Next
});
}
}
}
}
} ;
Dijkstra dij;
int n,Gasoline,a,b,c,map[205][205],ans=0x7fffffff;
int GetPos(int x,int y) { //二维坐标
return (x-1)*n+y;
}
int GetPos(int Gas,int x,int y) { //三维坐标
return (Gasoline-Gas)*n*n+GetPos(x,y);
}
void Build_Graph() { //按照油量分层
dij.init(n*n*(Gasoline+1));
for(int Gas=0; Gas<Gasoline; Gas++) { //油量
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(map[i][j])dij.AddEdge(GetPos(Gas,i,j),GetPos(Gasoline,i,j),a); //有加油站,向满油层连跨维边
else if(Gas==0)dij.AddEdge(GetPos(Gas,i,j),GetPos(Gasoline,i,j),a+c); //无加油站,向满油层连跨维边
}
const int Dirx[]= {0,1,0,-1,0},Diry[]= {0,0,1,0,-1};
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
for(int k=1; k<=4; k++) {
int Nextx=i+Dirx[k],Nexty=j+Diry[k];
if(Nextx<1||Nextx>n||Nexty<1||Nexty>n)continue;
if(map[i][j]) { //此处是加油站,从满油层连出跨维边
int Now=GetPos(Gasoline,i,j),Next=GetPos(Gasoline-1,Nextx,Nexty);
if(k==3||k==4)dij.AddEdge(Now,Next,b); //倒着走
else dij.AddEdge(Now,Next,0);
} else {
for(int Gas=1; Gas<=Gasoline; Gas++) {
int Now=GetPos(Gas,i,j),Next=GetPos(Gas-1,Nextx,Nexty);
if(k==3||k==4)dij.AddEdge(Now,Next,b); //倒着走
else dij.AddEdge(Now,Next,0);
}
}
}
}
int main() {
n=Get_Int();
Gasoline=Get_Int();
a=Get_Int();
b=Get_Int();
c=Get_Int();
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)map[i][j]=Get_Int();
Build_Graph();
int Start=GetPos(Gasoline,1,1);
dij.main(Start);
for(int i=0; i<=Gasoline; i++)ans=min(ans,dij.dist[GetPos(i,n,n)]);
printf("%d\n",ans);
return 0;
}