[网络流24题-15] 汽车加油行驶 - 分层图


题目描述

给定一个 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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.问描述 给定一个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均为正整数。 算法设计: 求汽车从起点出发到达终点的一条所付费用最少的行驶路线。 数据输入: 输入数据。第一行是N,K,A,B,C的值,2 <= N <= 100, 2 <= K <= 10。第二行起是一个N*N 的0-1方阵,每行N 个值,至N+1行结束。方阵的第i 行第j 列处的值为1 表示在网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。 各行相邻的2 个数以空格分隔。 结果输出: 将找到的最优行驶路线所需的费用,即最小费用输出. Sample input 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 Sample output 12

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值