• 【洛谷4009】汽车加油行驶问题(SPFA乱搞)


    点此看题面

    大致题意:给定一个(N*N)的方形网格,其中1表示这个格子有油库,0表示这个格子没油库,且汽车加满油可以行驶(k)条网格边。如果遇到油库必须加满油并花费(A)元,如果(X)坐标或(Y)坐标减少需花费(B)元,若需新建一个油库需花费(C)元(另需加油费(A)元)。问你从((1,1))((N,N))的最少花费。

    (SPFA)做法

    先说明,这篇博客只讲(SPFA),不讲网络流。

    我们可以用(dis_{i,j,l})来表示到达网格((i,j)),还能行驶(l)条网格边所需的最小花费。

    显然,初始化(dis_{1,1,k}=0),然后从((1,1))出发跑最短路即可。

    对于当前状态((i,j,l)),我们可以这样考虑它的转移:

    • 如果当前还能行驶的距离不等于(k)(即油未加满)

      • 如果当前网格有油库,那么我们就可以花费(A)元将状态转移至((i,j,k)),即:

        [dis_{i,j,k}=min(dis_{i,j,k},dis_{i,j,l}+A) ]

      • 如果当前网格没有油库,那么我们就可以花费(C)元造一个油库,然后花费(A)元将状态转移至((i,j,k)),即:

        [dis_{i,j,k}=min(dis_{i,j,k},dis_{i,j,l}+A+C) ]

    • 如果当前还能行驶的距离大于0

      • 如果是向右或向下行驶,那么可以直接将状态转移至((i+1,j,l-1))((i,j+1,l-1)),即:

      [dis_{i+1,j,l-1}=min(dis_{i+1,j,l-1},dis_{i,j,l}) ]

      [dis_{i,j+1,l-1}=min(dis_{i,j+1,l-1},dis_{i,j,l}) ]

      • 如果是向左或向上行驶,那么就需要花费(B)元才能将状态转移至((i-1,j,l-1))((i,j-1,l-1)),即:

        [dis_{i-1,j,l-1}=min(dis_{i-1,j,l-1},dis_{i,j,l}+B) ]

        [dis_{i,j-1,l-1}=min(dis_{i,j-1,l-1},dis_{i,j,l}+B) ]

    这样,代码就不难写了吧。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define abs(x) ((x)<0?-(x):(x))
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
    #define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))
    #define N 100
    #define K 10
    #define MOD (N*N*K)
    int pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
    using namespace std;
    const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
    int n,m,a,b,c,s[N+5][N+5],dis[N+5][N+5][K+5],Inqueue[N+5][N+5][K+5];
    struct Status
    {
        int x,y,v;
    }q[MOD+5];
    inline void read(int &x)
    {
        x=0;static char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
    }
    inline void read_digit(int &x)
    {
        while(!isdigit(x=tc()));
        x-=48;
    }
    inline void write(int x)
    {
        if(x>9) write(x/10);
        pc(x%10+'0');
    }
    int main()
    {
        register int i,j,l,H=Inqueue[1][1][m]=1,T=1;
        for(read(n),read(m),read(a),read(b),read(c),i=1;i<=n;++i) for(j=1;j<=n;++j) read_digit(s[i][j]);
        for(i=1;i<=n;++i) for(j=1;j<=n;++j) for(l=0;l<=m;++l) dis[i][j][l]=1e9;//初始化
        dis[1][1][m]=0,q[1]=(Status){1,1,m};//初始化
        while((H%=MOD)^(T+1))//只要队列不为空(因为是手写的循环队列,因此看起来特别别扭)
        {
            Status k=q[H++];Inqueue[k.x][k.y][k.v]=0;//取出队首元素
            if(k.v^m)//如果油未加满
            {
                if(s[k.x][k.y])//如果这里有油库
                {
                    if(dis[k.x][k.y][k.v]+a<dis[k.x][k.y][m]) 
                    {
                        dis[k.x][k.y][m]=dis[k.x][k.y][k.v]+a;
                        if(!Inqueue[k.x][k.y][m]) Inqueue[k.x][k.y][m]=1,q[(++T)%=MOD]=(Status){k.x,k.y,m};
                    } 
                    continue;
                }//否则就没有油库,需要花C元建一个
                if(dis[k.x][k.y][k.v]+a+c<dis[k.x][k.y][m])
                {
                    dis[k.x][k.y][m]=dis[k.x][k.y][k.v]+a+c;
                    if(!Inqueue[k.x][k.y][m]) Inqueue[k.x][k.y][m]=1,q[(++T)%=MOD]=(Status){k.x,k.y,m};
                }
            }
            if(k.v)//如果还有油
            {
                for(i=0;i<4;++i)//枚举上下左右四个方向
                {
                    static int nx,ny;
                    if(dis[k.x][k.y][k.v]+(dx[i]<0||dy[i]<0)*b<dis[nx=k.x+dx[i]][ny=k.y+dy[i]][k.v-1])//如果是向左或向上,还需加上B元
                    {
                        dis[nx][ny][k.v-1]=dis[k.x][k.y][k.v]+(dx[i]<0||dy[i]<0)*b;
                        if(!Inqueue[nx][ny][k.v-1]) Inqueue[nx][ny][k.v-1]=1,q[(++T)%=MOD]=(Status){nx,ny,k.v-1};
                    }
                }
            }
        }
        register int ans=1e9;
        for(i=0;i<=m;++i) ans=min(ans,dis[n][n][i]);//枚举到达(N,N)后还能行驶的距离,从而求出最优方案下的最少花费
        return write(ans),fwrite(pp,1,pp_,stdout),0;
    }
    
  • 相关阅读:
    LeetCode100-相同的树
    LeetCode66-加一
    LeetCode102-二叉树的层序遍历
    dubbo协议端口
    http错误-413 Request Entity Too Large
    【Jeecg Vue】通过getAction的finally来最大程度避免影响主数据呈现
    图片压缩,用这个就够了
    信息数据安全,日常办公你就要注意这些!
    java笔记:流式编程 数组与List集合互转
    改状态,你会改吗?你真的会改吗?
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4009.html
Copyright © 2020-2023  润新知