汽车加油行驶问题 分层图最短路

汽车加油行驶问题 分层图最短路

  1. 汽车只能沿网格边行驶,装满油后能行驶 \(K\) 条网格边。出发时汽车已装满油,在起点与终点处不设油库。
  2. 汽车经过一条网格边时,若其 \(X\) 坐标或 \(Y\) 坐标减小,则应付费用 \(B\) ,否则免付费用。
  3. 汽车在行驶过程中遇油库则应加满油并付加油费用 \(A\)
  4. 在需要时可在网格点处增设油库,并付增设油库费用\(C\)(不含加油费用\(A\) )。

\(k+1\)层图,表示汽车油量\(k\)的状态(油量\(0\cdots k\)),然后根据具体规则在其上建图以转移状态。规定第\(k\)层(从\(0\)开始)第\(i\)行第\(j\)列点编号为\(k*n*n+(i-1)*n+j\),然后起点即为\(k*n*n+1\),新建一个超级终点,将每层图的\(n*n\)这个点建一条边权为\(0\)的边指向这个超级终点即可。

跑最短路的过程相当于状态转移求解\(DP\)的过程。

#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
#define MAXN 110*110
#define MAXK 13
#define MAXM MAXN*4
inline int read(){
    char ch=getchar();int s=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') s=s*10+(ch^'0'), ch=getchar();
    return s;
}
int head[MAXN*MAXK],nxt[MAXM*MAXK],vv[MAXM*MAXK],ww[MAXM*MAXK],tot;
inline void add_edge(int u, int v, int w){
    vv[++tot]=v;
    ww[tot]=w;
    nxt[tot]=head[u];
    head[u]=tot;
}
bool ins[MAXN*MAXK];
int dis[MAXN*MAXK];
queue <int> q;
void spfa(int s){
    memset(dis, 0x3f, sizeof dis);
    memset(ins, 0, sizeof ins);
    while(!q.empty()) q.pop();
    dis[s]=0;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        ins[u]=0;
        for(register int i=head[u];i;i=nxt[i]){
            int v=vv[i],w=ww[i];
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!ins[v]) q.push(v),ins[v]=1;
            }
        }
    }
}
int n,k,cost_a,cost_b,cost_c;
inline void add(int ux, int uy, int uz, int vx, int vy, int vz, int w){
    add_edge((ux-1)*n+uy+uz*n*n, (vx-1)*n+vy+vz*n*n, w);
}
int main(){
    n=read(),k=read(),cost_a=read(),cost_b=read(),cost_c=read();
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=n;++j){
            int rd=read();
            if(rd==0){
                for(int t=0;t<k;++t) add(i, j, t, i, j, k, cost_a+cost_c);
                for(int t=1;t<=k;++t){
                    if(i>=2) add(i, j, t, i-1, j, t-1, cost_b);
                    if(j>=2) add(i, j, t, i, j-1, t-1, cost_b);
                    if(i<=n-1) add(i, j, t, i+1, j, t-1, 0);
                    if(j<=n-1) add(i, j, t, i, j+1, t-1, 0);
                }
            }else{
                for(int t=0;t<k;++t) add(i, j, t, i, j, k, cost_a);
              	// 有加油站的必须加满油,所以只在k层图上建边
                if(i>=2) add(i, j, k, i-1, j, k-1, cost_b);
                if(j>=2) add(i, j, k, i, j-1, k-1, cost_b);
                if(i<=n-1) add(i, j, k, i+1, j, k-1, 0);
                if(j<=n-1) add(i, j, k, i, j+1, k-1, 0);
            }
        }
    for(int i=0;i<=k;++i)
        add(n, n, i, 1, 1, k+1, 0); // 超级终点
    spfa(1+n*n*k);
    printf("%d", dis[1+n*n*(k+1)]);
    return 0;
}
posted @ 2019-11-05 15:47  Santiego  阅读(136)  评论(0编辑  收藏  举报